Files
pbakaus_impeccable/tests/oracle/vectors/calls/rules.checks/checkHtmlPatterns.jsonl
T
Paul Bakaus b6a34de55a Tests: gate behavior on the oracle and the engine binary
Unit tests of the deleted Node scripts and the JS detector are removed;
their behavior is pinned by tests/oracle goldens (frozen JS behavior plus
reviewed deltas) and the engine's own tests. tests/oracle.test.mjs replays
the corpus against the binary (IMPECCABLE_BIN or skill/scripts/bin/<target>/,
via tests/lib/engine-bin.mjs) and skips cleanly without one; the framework
fixture sweep drives live-inject, live-wrap, and detect-csp through the
binary the same way. record.mjs learns --bin. The function-level vectors
under tests/oracle/vectors/calls are committed as the frozen snapshot they
can no longer be regenerated from. Suites: core trimmed to build and
transformer tests, oracle added to the default run, detector/live reduced to
packaging and reference checks, the live-e2e helper tests move to the opt-in
live-e2e lane pending its retarget, cli-remote-e2e is an empty placeholder.

Prepared with AI assistance (Claude Code).
2026-08-31 19:59:20 -07:00

102 lines
798 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"args":["<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>Blinking cursor fixture</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: monospace, monospace; font-size: 16px; }\n .hero { padding: 40px; }\n .terminal { background: #111; color: #eee; padding: 24px; width: 600px; }\n\n @keyframes blink-anim { 50% { opacity: 0; } }\n @keyframes flicker { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }\n @keyframes caret-blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* FLAG: solid block cursor in the hero terminal */\n .flag-block-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #33d17a; vertical-align: text-bottom;\n animation: blink-anim 1.1s steps(1) infinite;\n }\n\n /* FLAG: glyph cursor, animation name says nothing but keyframes toggle opacity */\n .flag-glyph-cursor { animation: flicker 1s steps(2) infinite; }\n\n /* FLAG: underscore bar cursor */\n .flag-underscore-cursor {\n display: inline-block; width: 12px; height: 3px;\n background: #eee; vertical-align: baseline;\n animation: caret-blink 0.9s steps(1) infinite;\n }\n\n /* PASS: same block cursor shape, but deep below the landing region */\n .pass-below-fold-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #33d17a;\n animation: blink-anim 1.1s steps(1) infinite;\n }\n\n /* PASS: cursor inside real editable surfaces */\n .pass-editable-cursor, .pass-textbox-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #444;\n animation: blink-anim 1s steps(1) infinite;\n }\n\n /* PASS: round pulsing dot stays with the pulsing-dot rule */\n .pass-round-dot {\n display: inline-block; width: 8px; height: 8px;\n background: #33d17a; border-radius: 50%;\n animation: blink-anim 2s steps(1) infinite;\n }\n\n /* PASS: spinner animates transform, not visibility */\n .pass-spinner {\n display: inline-block; width: 16px; height: 16px;\n border: 2px solid #999; border-top-color: #333; border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n /* PASS: a blinking word is not a cursor shape */\n .pass-blinking-word { animation: flicker 1s steps(2) infinite; }\n\n /* PASS: large blinking panel fails the caret-size gates */\n .pass-blinking-panel {\n display: inline-block; width: 200px; height: 90px;\n background: #222;\n animation: blink-anim 2s steps(1) infinite;\n }\n\n .spacer { height: 1400px; }\n</style>\n</head>\n<body>\n\n <div class=\"hero\">\n <h1>Terminal-flavored landing hero</h1>\n <div class=\"terminal\">\n <p>user@host:~$ run demo <span class=\"flag-block-cursor\" aria-hidden=\"true\"></span></p>\n <p>user@host:~$ tail -f logs <span class=\"flag-glyph-cursor\" aria-hidden=\"true\">▌</span></p>\n <p>user@host:~$ next up <span class=\"flag-underscore-cursor\" aria-hidden=\"true\"></span></p>\n </div>\n\n <p style=\"margin-top: 24px;\">Status <span class=\"pass-round-dot\" aria-hidden=\"true\"></span>\n loading <span class=\"pass-spinner\" aria-hidden=\"true\"></span>\n and a <span class=\"pass-blinking-word\">LIVE</span> badge word\n plus a big panel <span class=\"pass-blinking-panel\" aria-hidden=\"true\"></span></p>\n\n <div contenteditable=\"true\" class=\"terminal\" style=\"margin-top: 24px;\">\n <p>editable prompt <span class=\"pass-editable-cursor\" aria-hidden=\"true\"></span></p>\n </div>\n\n <div role=\"textbox\" class=\"terminal\" style=\"margin-top: 24px;\">\n <p>textbox prompt <span class=\"pass-textbox-cursor\" aria-hidden=\"true\"></span></p>\n </div>\n </div>\n\n <div class=\"spacer\"></div>\n\n <div class=\"terminal\">\n <p>deep footer terminal <span class=\"pass-below-fold-cursor\" aria-hidden=\"true\"></span></p>\n </div>\n\n</body>\n</html>\n",{"styleText":"\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: monospace, monospace; font-size: 16px; }\n .hero { padding: 40px; }\n .terminal { background: #111; color: #eee; padding: 24px; width: 600px; }\n\n @keyframes blink-anim { 50% { opacity: 0; } }\n @keyframes flicker { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }\n @keyframes caret-blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* FLAG: solid block cursor in the hero terminal */\n .flag-block-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #33d17a; vertical-align: text-bottom;\n animation: blink-anim 1.1s steps(1) infinite;\n }\n\n /* FLAG: glyph cursor, animation name says nothing but keyframes toggle opacity */\n .flag-glyph-cursor { animation: flicker 1s steps(2) infinite; }\n\n /* FLAG: underscore bar cursor */\n .flag-underscore-cursor {\n display: inline-block; width: 12px; height: 3px;\n background: #eee; vertical-align: baseline;\n animation: caret-blink 0.9s steps(1) infinite;\n }\n\n /* PASS: same block cursor shape, but deep below the landing region */\n .pass-below-fold-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #33d17a;\n animation: blink-anim 1.1s steps(1) infinite;\n }\n\n /* PASS: cursor inside real editable surfaces */\n .pass-editable-cursor, .pass-textbox-cursor {\n display: inline-block; width: 10px; height: 18px;\n background: #444;\n animation: blink-anim 1s steps(1) infinite;\n }\n\n /* PASS: round pulsing dot stays with the pulsing-dot rule */\n .pass-round-dot {\n display: inline-block; width: 8px; height: 8px;\n background: #33d17a; border-radius: 50%;\n animation: blink-anim 2s steps(1) infinite;\n }\n\n /* PASS: spinner animates transform, not visibility */\n .pass-spinner {\n display: inline-block; width: 16px; height: 16px;\n border: 2px solid #999; border-top-color: #333; border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n /* PASS: a blinking word is not a cursor shape */\n .pass-blinking-word { animation: flicker 1s steps(2) infinite; }\n\n /* PASS: large blinking panel fails the caret-size gates */\n .pass-blinking-panel {\n display: inline-block; width: 200px; height: 90px;\n background: #222;\n animation: blink-anim 2s steps(1) infinite;\n }\n\n .spacer { height: 1400px; }\n\nstyle=\"margin-top: 24px;\"\nstyle=\"margin-top: 24px;\"\nstyle=\"margin-top: 24px;\"","classText":"hero\nterminal\nflag-block-cursor\nflag-glyph-cursor\nflag-underscore-cursor\npass-round-dot\npass-spinner\npass-blinking-word\npass-blinking-panel\nterminal\npass-editable-cursor\nterminal\npass-textbox-cursor\nspacer\nterminal\npass-below-fold-cursor"}],"result":[{"id":"pulsing-dot","snippet":".pass-round-dot — 8x8px dot with infinite \"blink-anim\" animation","selector":".pass-round-dot"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Body text viewport edge — Should Flag vs Should Pass</title>\n <style>\n /* Fixture for the body-text-viewport-edge rule. The rule fires\n when a <p> or <li> with substantial text content has rect.left\n within 16px of the viewport edge (or rect.right within 16px\n of viewport.width) AND the element has no own background-color\n AND is not inside <nav>/<header> AND is in normal flow. */\n body { font: 14px/1.5 system-ui, sans-serif; margin: 0; color: #111; background: #fff; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; padding: 24px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font: 600 14px/1 system-ui; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin: 16px 0 4px; font-style: italic; }\n .container-good { max-width: 720px; margin: 0 auto; padding: 16px 32px; }\n .container-tight { padding: 16px 32px; }\n .full-bleed-section { background: #fef3c7; padding: 24px 16px; }\n nav.site-nav { padding: 12px 24px; background: #f5f5f5; }\n header.banner { padding: 12px 24px; background: #e5e7eb; }\n\n /* Override the grid container's own padding so we can demonstrate\n a paragraph that genuinely bleeds to the viewport edge inside\n the flag column — the col itself shouldn't reset its padding. */\n .flag-fullwidth-p { /* no container; placed directly in body via .escape */ }\n .escape { position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; width: 100vw; padding: 0; }\n .edge-list { margin: 0; padding: 0; list-style-position: inside; }\n .edge-list li { margin: 0; }\n </style>\n</head>\n<body>\n\n <!-- ════════════════════════════════════════════════════════════\n FLAG CASES — body text bleeds to viewport edge.\n These paragraphs are placed OUTSIDE the .grid container so\n their bounding rect actually touches the viewport edges.\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"escape\">\n <p>Lucia opened this place in 1978. She cooked every night for fifty years, making the same dishes: spaghetti carbonara, eggplant parmigiana, veal saltimbocca, fresh lasagna. She did not change a single recipe in all that time. Her grandson Anthony runs the kitchen now, using the same recipes with the same care.</p>\n </div>\n <div class=\"escape\">\n <p>This second flush-to-edge paragraph confirms the rule fires on every body paragraph that touches the viewport, not just one. The rendered width should be the full viewport, with text starting essentially at x=0.</p>\n </div>\n <div class=\"escape\">\n <ul class=\"edge-list\">\n <li>This is a list item whose text content is substantially long enough to qualify as body content. It also runs flush against the viewport edge with no left padding.</li>\n </ul>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n PASS CASES — body text with adequate container padding.\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"grid\">\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <span class=\"case-label\">paragraph in centered container with 32px padding</span>\n <div class=\"container-good\">\n <p>This paragraph sits inside a max-width container that's centered and has 32px horizontal padding. It does not touch the viewport edges and should not trigger the rule.</p>\n </div>\n\n <span class=\"case-label\">paragraph inside &lt;nav&gt; (excluded — nav can bleed)</span>\n <nav class=\"site-nav\">\n <p>This nav legitimately bleeds to the edges as a full-width header strip; the rule excludes elements inside &lt;nav&gt; or &lt;header&gt; so this does not flag.</p>\n </nav>\n\n <span class=\"case-label\">paragraph inside &lt;header&gt; (excluded — header can bleed)</span>\n <header class=\"banner\">\n <p>Banner headers commonly bleed to viewport edges with their own internal layout. The rule excludes &lt;header&gt; descendants.</p>\n </header>\n\n <span class=\"case-label\">paragraph inside full-bleed section with own background</span>\n <section class=\"full-bleed-section\">\n <p>This paragraph sits in a full-bleed section that has its own background-color set (intentional design move). The rule excludes paragraphs whose element has its own background-color.</p>\n </section>\n\n <span class=\"case-label\">short label / button-ish text (excluded — &lt; 40 chars)</span>\n <p class=\"container-tight\">Short label.</p>\n </div>\n\n <div class=\"col\" data-col=\"flag-notes\">\n <h2>Notes</h2>\n <p>The flag cases above (rendered outside this grid) demonstrate the failure. The pass cases here show acceptable patterns. The rule gates aggressively to avoid false positives: it only flags &lt;p&gt; and &lt;li&gt; with &gt;40 chars whose width spans more than 50% of the viewport AND whose rect.left or rect.right is within 16px of a viewport edge AND which are not inside nav/header AND which have no own background-color AND which are in normal flow (not position:fixed/absolute).</p>\n </div>\n </div>\n\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Fixture for the body-text-viewport-edge rule. The rule fires\n when a <p> or <li> with substantial text content has rect.left\n within 16px of the viewport edge (or rect.right within 16px\n of viewport.width) AND the element has no own background-color\n AND is not inside <nav>/<header> AND is in normal flow. */\n body { font: 14px/1.5 system-ui, sans-serif; margin: 0; color: #111; background: #fff; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; padding: 24px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font: 600 14px/1 system-ui; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin: 16px 0 4px; font-style: italic; }\n .container-good { max-width: 720px; margin: 0 auto; padding: 16px 32px; }\n .container-tight { padding: 16px 32px; }\n .full-bleed-section { background: #fef3c7; padding: 24px 16px; }\n nav.site-nav { padding: 12px 24px; background: #f5f5f5; }\n header.banner { padding: 12px 24px; background: #e5e7eb; }\n\n /* Override the grid container's own padding so we can demonstrate\n a paragraph that genuinely bleeds to the viewport edge inside\n the flag column — the col itself shouldn't reset its padding. */\n .flag-fullwidth-p { /* no container; placed directly in body via .escape */ }\n .escape { position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; width: 100vw; padding: 0; }\n .edge-list { margin: 0; padding: 0; list-style-position: inside; }\n .edge-list li { margin: 0; }\n ","classText":"escape\nescape\nescape\nedge-list\ngrid\ncol\ncase-label\ncontainer-good\ncase-label\nsite-nav\ncase-label\nbanner\ncase-label\nfull-bleed-section\ncase-label\ncontainer-tight\ncol"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Border Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f8fafc;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1180px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n font-weight: 720;\n }\n\n .intro {\n max-width: 1180px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .case {\n margin: 0 0 14px;\n padding: 16px;\n background: #fdfdfd;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);\n }\n\n .case h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .case p {\n margin: 0;\n color: #475569;\n }\n\n .flag-left {\n border-left: 4px solid #2563eb;\n border-radius: 10px;\n }\n\n .flag-right {\n border-right: 5px solid #dc2626;\n border-radius: 10px;\n }\n\n .flag-left-plain {\n border-left: 4px solid #0f766e;\n }\n\n .flag-top {\n border-top: 4px solid #7c3aed;\n border-radius: 10px;\n }\n\n .flag-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 10px;\n }\n\n .flag-dark {\n background: #141820;\n color: #f4f7fb;\n border-left: 4px solid #38bdf8;\n border-radius: 10px;\n }\n\n .flag-dark p {\n color: #cbd5e1;\n }\n\n .pass-full-border {\n border: 1px solid #cbd5e1;\n border-radius: 10px;\n }\n\n .pass-neutral-side {\n border-left: 4px solid #d1d5db;\n border-radius: 10px;\n }\n\n .pass-thin-side {\n border-left: 1px solid #2563eb;\n border-radius: 10px;\n }\n\n .pass-status-alert {\n border-left: 4px solid #2563eb;\n }\n\n .flag-square-top {\n border-top: 4px solid #7c3aed;\n border-radius: 0;\n }\n\n .flag-square-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 0;\n }\n\n .pass-tab-underline {\n border-bottom: 3px solid #2563eb;\n border-radius: 0;\n }\n\n .pass-thick-band {\n border-top: 16px solid #7c3aed;\n border-radius: 0;\n }\n\n .pass-dark-uniform {\n background: #161a22;\n color: #f4f7fb;\n border: 1px solid #334155;\n border-radius: 10px;\n }\n\n .pass-dark-uniform p {\n color: #cbd5e1;\n }\n </style>\n</head>\n<body>\n <h1>Border side-tab detector cases</h1>\n <p class=\"intro\">A paired fixture for colored side borders and rounded accent borders. The left column should produce border findings; the right column should stay clean.</p>\n\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <div class=\"case flag-left\">\n <h3>Rounded card with colored left border</h3>\n <p>Classic side tab: border-left is thick, colored, and paired with rounded corners.</p>\n </div>\n\n <div class=\"case flag-right\">\n <h3>Rounded card with colored right border</h3>\n <p>The right-side version is the same visual trope mirrored.</p>\n </div>\n\n <div class=\"case flag-left-plain\">\n <h3>Thick colored side border without radius</h3>\n <p>A thick chromatic side stripe still reads as a tab even without rounded corners.</p>\n </div>\n\n <div class=\"case flag-top\">\n <h3>Rounded card with top accent border</h3>\n <p>A heavy top accent on a rounded card is the horizontal variant of the same pattern.</p>\n </div>\n\n <div class=\"case flag-bottom\">\n <h3>Rounded card with bottom accent border</h3>\n <p>The bottom accent is also a decorative stripe on a card surface.</p>\n </div>\n\n <div class=\"case flag-dark\">\n <h3>Dark card with bright side stripe</h3>\n <p>Dark surfaces can hide the cliche, but the thick colored side stripe remains.</p>\n </div>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <div class=\"case pass-full-border\">\n <h3>Uniform full border</h3>\n <p>A quiet all-around border frames the card instead of creating a side tab.</p>\n </div>\n\n <div class=\"case pass-neutral-side\">\n <h3>Neutral structural side rule</h3>\n <p>A gray structural rule can be a legitimate divider when it is not chromatic emphasis.</p>\n </div>\n\n <div class=\"case pass-thin-side\">\n <h3>Thin colored side rule</h3>\n <p>A 1px colored rule is below the decorative side-tab threshold.</p>\n </div>\n\n <div class=\"case pass-status-alert\" role=\"status\">\n <h3>Status toast with severity accent</h3>\n <p>A live status region carries a colored side bar as a severity accent, not a decorative side tab.</p>\n </div>\n\n <div class=\"case flag-square-top\">\n <h3>Square top border</h3>\n <p>A chromatic top stripe on a card is the horizontal variant of the side tab.</p>\n </div>\n\n <div class=\"case flag-square-bottom\">\n <h3>Square bottom border</h3>\n <p>A chromatic bottom stripe on a badge or card is the same decorative tell.</p>\n </div>\n\n <div class=\"case\" role=\"tablist\">\n <div class=\"case pass-tab-underline\" role=\"tab\" aria-selected=\"true\">\n <h3>Selected tab underline</h3>\n <p>An active-state underline inside a tablist is an affordance, not a stripe.</p>\n </div>\n </div>\n\n <div class=\"case pass-thick-band\">\n <h3>Thick color band</h3>\n <p>A band well past stripe thickness is a compositional block, not an accent stripe.</p>\n </div>\n\n <div class=\"case pass-dark-uniform\">\n <h3>Dark card with uniform border</h3>\n <p>The dark surface has a full neutral outline, not a chromatic side stripe.</p>\n </div>\n </section>\n </div>\n\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f8fafc;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1180px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n font-weight: 720;\n }\n\n .intro {\n max-width: 1180px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .case {\n margin: 0 0 14px;\n padding: 16px;\n background: #fdfdfd;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);\n }\n\n .case h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .case p {\n margin: 0;\n color: #475569;\n }\n\n .flag-left {\n border-left: 4px solid #2563eb;\n border-radius: 10px;\n }\n\n .flag-right {\n border-right: 5px solid #dc2626;\n border-radius: 10px;\n }\n\n .flag-left-plain {\n border-left: 4px solid #0f766e;\n }\n\n .flag-top {\n border-top: 4px solid #7c3aed;\n border-radius: 10px;\n }\n\n .flag-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 10px;\n }\n\n .flag-dark {\n background: #141820;\n color: #f4f7fb;\n border-left: 4px solid #38bdf8;\n border-radius: 10px;\n }\n\n .flag-dark p {\n color: #cbd5e1;\n }\n\n .pass-full-border {\n border: 1px solid #cbd5e1;\n border-radius: 10px;\n }\n\n .pass-neutral-side {\n border-left: 4px solid #d1d5db;\n border-radius: 10px;\n }\n\n .pass-thin-side {\n border-left: 1px solid #2563eb;\n border-radius: 10px;\n }\n\n .pass-status-alert {\n border-left: 4px solid #2563eb;\n }\n\n .flag-square-top {\n border-top: 4px solid #7c3aed;\n border-radius: 0;\n }\n\n .flag-square-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 0;\n }\n\n .pass-tab-underline {\n border-bottom: 3px solid #2563eb;\n border-radius: 0;\n }\n\n .pass-thick-band {\n border-top: 16px solid #7c3aed;\n border-radius: 0;\n }\n\n .pass-dark-uniform {\n background: #161a22;\n color: #f4f7fb;\n border: 1px solid #334155;\n border-radius: 10px;\n }\n\n .pass-dark-uniform p {\n color: #cbd5e1;\n }\n ","classText":"intro\ngrid\ncol\ncase flag-left\ncase flag-right\ncase flag-left-plain\ncase flag-top\ncase flag-bottom\ncase flag-dark\ncol\ncase pass-full-border\ncase pass-neutral-side\ncase pass-thin-side\ncase pass-status-alert\ncase flag-square-top\ncase flag-square-bottom\ncase\ncase pass-tab-underline\ncase pass-thick-band\ncase pass-dark-uniform"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Clipped overflow container fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .box { position: relative; width: 200px; height: 120px; margin: 0 0 24px; padding: 16px; border: 1px solid #ddd; }\n .pop { position: absolute; top: 100%; left: 0; width: 220px; background: #222; color: #fff; padding: 8px; }\n .tip-negative { position: absolute; top: -34px; left: 16px; width: 160px; background: #222; color: #fff; padding: 8px; }\n .flyout { position: absolute; top: 28px; left: 100%; width: 180px; background: #222; color: #fff; padding: 8px; }\n .contained-badge { position: absolute; top: 10px; right: 10px; width: 36px; height: 24px; background: #e5f0ff; }\n .button-shine { position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); }\n .crop-photo { position: absolute; inset: -18px; background: linear-gradient(135deg, #b7d7ff, #486aa3); }\n .inside-overlay { position: absolute; left: 12px; right: 12px; bottom: 12px; min-height: 28px; background: rgba(255,255,255,.86); }\n .pass-hidden-button { position: relative; overflow: hidden; width: 160px; height: 52px; border: 1px solid #ddd; background: #111; color: #fff; }\n .pass-carousel-viewport,\n .pass-fisheye-list,\n .pass-split-container {\n position: relative;\n overflow: hidden;\n width: 200px;\n height: 120px;\n margin: 0 0 24px;\n border: 1px solid #ddd;\n }\n .carousel-slide-next { position: absolute; top: 100%; left: 0; width: 100%; height: 100%; padding: 16px; background: #f5f5f5; }\n .fisheye-next-item { position: absolute; top: 100%; left: 0; width: 100%; padding: 8px; background: none; border: 0; text-align: left; }\n .split-after-panel { position: absolute; top: 0; left: 50%; width: 100%; height: 100%; background: #f5f5f5; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: clipping container with a positioned child that needs to escape -->\n <section class=\"col flag\">\n <div class=\"box flag-overflow-hidden\" style=\"overflow: hidden\">\n <button>Menu</button>\n <ul class=\"pop\">Dropdown that gets clipped by the hidden ancestor</ul>\n </div>\n <div class=\"box flag-overflow-clip\" style=\"overflow: clip\">\n <button>Tooltip host</button>\n <span class=\"pop\">Tooltip clipped by overflow clip</span>\n </div>\n <div class=\"box flag-overflow-negative\" style=\"overflow: hidden\">\n <button>Help</button>\n <span class=\"tip-negative\">Tooltip clipped above the host</span>\n </div>\n <div class=\"box flag-overflow-right\" style=\"overflow: hidden\">\n <button>Actions</button>\n <div class=\"flyout\">Flyout menu clipped to the right of the host</div>\n </div>\n <div class=\"box flag-shadow-utility\" style=\"overflow: hidden\">\n <button>More</button>\n <div class=\"pop bg-white shadow-lg\">Dropdown with utility classes still gets clipped.</div>\n </div>\n <div class=\"box flag-overlay-surface\" style=\"overflow: clip\">\n <button>Dialog</button>\n <div class=\"pop modal-overlay\">Overlay-style dialog content still needs to escape.</div>\n </div>\n </section>\n\n <!-- PASS column: no clipping, no positioned child, real scroll region, or contained decorative positioning -->\n <section class=\"col pass\">\n <div class=\"box pass-hidden-no-abs\" style=\"overflow: hidden\">\n <p style=\"position: static\">Hidden container with only static content.</p>\n </div>\n <div class=\"box pass-visible-abs\" style=\"overflow: visible\">\n <button>Visible host</button>\n <span class=\"pop\">Positioned child can escape because overflow is visible.</span>\n </div>\n <div class=\"box pass-scroll-abs\" style=\"overflow: auto\">\n <button>Scroll host</button>\n <span class=\"pop\">A genuine scroll region is allowed to contain positioned children.</span>\n </div>\n <div class=\"box pass-contained-abs\" style=\"overflow: hidden\">\n <p>Positioned decoration remains inside the clipping box.</p>\n <span class=\"contained-badge\" aria-hidden=\"true\"></span>\n </div>\n <button type=\"button\" class=\"pass-hidden-button pass-button-shine\">\n Save\n <span class=\"button-shine\" aria-hidden=\"true\"></span>\n </button>\n <div class=\"box pass-crop-photo\" style=\"overflow: hidden\">\n <span class=\"crop-photo\" aria-hidden=\"true\"></span>\n </div>\n <div class=\"box pass-contained-overlay\" style=\"overflow: clip\">\n <p>Mockup frame with a contained absolute overlay.</p>\n <span class=\"inside-overlay\" aria-hidden=\"true\"></span>\n </div>\n <div class=\"pass-carousel-viewport\" aria-roledescription=\"carousel\">\n <div class=\"carousel-slide-next\">A hidden next slide is clipped intentionally by the viewport.</div>\n </div>\n <div class=\"pass-fisheye-list\">\n <button type=\"button\" class=\"fisheye-next-item\">Next command</button>\n </div>\n <div class=\"pass-split-container\">\n <div class=\"split-after-panel\">Before/after comparison panel clipped by the split frame.</div>\n </div>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .box { position: relative; width: 200px; height: 120px; margin: 0 0 24px; padding: 16px; border: 1px solid #ddd; }\n .pop { position: absolute; top: 100%; left: 0; width: 220px; background: #222; color: #fff; padding: 8px; }\n .tip-negative { position: absolute; top: -34px; left: 16px; width: 160px; background: #222; color: #fff; padding: 8px; }\n .flyout { position: absolute; top: 28px; left: 100%; width: 180px; background: #222; color: #fff; padding: 8px; }\n .contained-badge { position: absolute; top: 10px; right: 10px; width: 36px; height: 24px; background: #e5f0ff; }\n .button-shine { position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); }\n .crop-photo { position: absolute; inset: -18px; background: linear-gradient(135deg, #b7d7ff, #486aa3); }\n .inside-overlay { position: absolute; left: 12px; right: 12px; bottom: 12px; min-height: 28px; background: rgba(255,255,255,.86); }\n .pass-hidden-button { position: relative; overflow: hidden; width: 160px; height: 52px; border: 1px solid #ddd; background: #111; color: #fff; }\n .pass-carousel-viewport,\n .pass-fisheye-list,\n .pass-split-container {\n position: relative;\n overflow: hidden;\n width: 200px;\n height: 120px;\n margin: 0 0 24px;\n border: 1px solid #ddd;\n }\n .carousel-slide-next { position: absolute; top: 100%; left: 0; width: 100%; height: 100%; padding: 16px; background: #f5f5f5; }\n .fisheye-next-item { position: absolute; top: 100%; left: 0; width: 100%; padding: 8px; background: none; border: 0; text-align: left; }\n .split-after-panel { position: absolute; top: 0; left: 50%; width: 100%; height: 100%; background: #f5f5f5; }\n \nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"\nstyle=\"overflow: hidden\"\nstyle=\"position: static\"\nstyle=\"overflow: visible\"\nstyle=\"overflow: auto\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"","classText":"cols\ncol flag\nbox flag-overflow-hidden\npop\nbox flag-overflow-clip\npop\nbox flag-overflow-negative\ntip-negative\nbox flag-overflow-right\nflyout\nbox flag-shadow-utility\npop bg-white shadow-lg\nbox flag-overlay-surface\npop modal-overlay\ncol pass\nbox pass-hidden-no-abs\nbox pass-visible-abs\npop\nbox pass-scroll-abs\npop\nbox pass-contained-abs\ncontained-badge\npass-hidden-button pass-button-shine\nbutton-shine\nbox pass-crop-photo\ncrop-photo\nbox pass-contained-overlay\ninside-overlay\npass-carousel-viewport\ncarousel-slide-next\npass-fisheye-list\nfisheye-next-item\npass-split-container\nsplit-after-panel"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Color Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n /* Styled button color cases (declared via stylesheet so jsdom resolves them\n reliably — inline color doesn't always win over user-agent rules in jsdom\n for <a> and <button>). */\n .styled-pill-low { background-color: rgb(31, 26, 21); color: rgb(91, 79, 68); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .low-contrast-button { background-color: rgb(55, 65, 81); color: rgb(108, 114, 128); display: inline-block; padding: 10px 20px; border-radius: 8px; border: 0; font-size: 14px; }\n .good-pill-high { background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .inline-link-low { color: rgb(170, 170, 170); }\n /* Chip / badge cases: text-bearing spans painting their own background. */\n .chip-sev-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(92, 84, 73); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-ok-high { display: inline-block; background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-sub9-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(150, 60, 55); font-size: 8px; padding: 1px 4px; }\n .panel-reset { background: rgb(28, 30, 38); color: rgb(230, 232, 237); padding: 12px; }\n .panel-reset code { background: rgb(246, 242, 244); border-radius: 3px; padding: 1px 4px; }\n .panel-reset pre code { background: none; }\n /* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill\n (text-fill-color: transparent), not a backdrop. The inherited `color`\n (#e8e6e3) is never painted, so measuring it against the element's own\n gradient stops (#6d8cff / #a78bfa) is a false positive. */\n .ox-grad-text { background: linear-gradient(135deg, #6d8cff 0%, #a78bfa 50%, #6d8cff 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: #e8e6e3; font-size: 40px; font-weight: 800; }\n /* issue #409 Case B: a 9%-alpha accent glow stop over a dark surface. The\n stop composites to ~#121f1f, not the full-opacity #34c0a8, so text stays\n high-contrast. The dark wrapper supplies the surface beneath the glow. */\n .ox-dark-wrap { background: #0f0f11; padding: 16px; }\n .ox-glow { background: linear-gradient(160deg, rgba(52,192,168,0.09) 0%, #141419 65%); padding: 20px; }\n .ox-glow p { color: #e8e6e3; font-size: 18px; }\n .ox-glow .muted { color: #8e8c89; font-size: 16px; }\n /* Gradient stop written as color-mix with a bright nested ingredient.\n The stop paints as a 16% wash of the light oklch color (composited\n over the dark wrap it is near-black); the nested oklch(90% ...) is an\n INGREDIENT, never painted. A parser that re-extracts nested tokens\n appends it as a phantom opaque light stop and the worst-case ratio\n then flags the light text at ~1:1 against a color nobody sees. */\n .mix-dark-wrap { background: #0f0f11; padding: 16px; }\n .mix-glow { background: linear-gradient(160deg, color-mix(in oklab, oklch(90% 0.02 95) 16%, transparent) 0%, #141419 65%); padding: 20px; }\n .mix-glow p { color: #ded9cf; font-size: 16px; }\n /* currentcolor surface: background-color paints with the element's own\n text color, which is itself a var() token here. jsdom hands both\n through verbatim, so the walk must resolve the token via the\n custom-prop map instead of abstaining on a knowable surface. */\n :root { --fixture-bone: #e8e2d6; }\n .currentcolor-surface { background-color: currentcolor; color: var(--fixture-bone); padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .currentcolor-low-text { color: #cfc9bd; font-size: 14px; }\n .currentcolor-good-text { color: #3a352c; font-size: 14px; }\n /* Same shape but the token does not exist: the surface truly cannot be\n read, so the walk must abstain rather than guess. */\n .currentcolor-unknown { background-color: currentcolor; color: var(--fixture-undefined-token); padding: 14px 16px; border-radius: 10px; }\n .currentcolor-unknown p { color: #efe9dd; font-size: 14px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Pure black surfaces</h3>\n <div class=\"card\" style=\"background: #000000; color: white;\">\n <p>Pure #000 background</p>\n </div>\n\n <h3>Gray on color</h3>\n <div class=\"card\" style=\"background: rgb(59, 130, 246);\">\n <p style=\"color: rgb(150, 150, 150);\">Gray text on blue background</p>\n </div>\n <div class=\"card\" style=\"background: rgb(16, 185, 129);\">\n <p style=\"color: rgb(180, 180, 180);\">Gray text on green background</p>\n </div>\n\n <h3>Low contrast</h3>\n <div class=\"card\" style=\"background: white;\">\n <p style=\"color: rgb(200, 200, 200);\">Light gray text on white — very low contrast</p>\n </div>\n <div class=\"card\" style=\"background: rgb(30, 30, 30);\">\n <p style=\"color: rgb(80, 80, 80);\">Dark gray text on near-black — low contrast</p>\n </div>\n <!-- Gradient background — gray heading is unreadable across every stop -->\n <div class=\"card\" style=\"background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;\">\n <h1 style=\"color: #808080; font-size: 32px; font-weight: 800; margin: 0;\">Welcome to Our Platform</h1>\n <p style=\"color: #666666; font-size: 14px;\">The best solution for modern teams and businesses</p>\n </div>\n\n <h3>Gradient text</h3>\n <h3 style=\"background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;\">\n Gradient Heading\n </h3>\n\n <h3>AI color palette</h3>\n <h1 style=\"color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;\">Purple heading text</h1>\n\n <h3>Styled &lt;a&gt; and &lt;button&gt; with low contrast</h3>\n <!-- A pill-style <a> with its own opaque background and low-contrast text.\n Currently SAFE_TAGS skips <a> entirely; this case asserts the contrast\n check still runs when the element is styled as a button. Mirrors a real\n bug from the landing-demo: warm-charcoal text on near-black bg. -->\n <a href=\"#\" class=\"styled-pill-low\" data-test=\"styled-pill\">Get started</a>\n <!-- A <button> element with its own opaque background and low-contrast text.\n Same SAFE_TAGS exception — buttons styled with their own background should\n still pass through the contrast check. -->\n <button class=\"low-contrast-button\" data-test=\"low-contrast-button\">Submit</button>\n\n <h3>Text-bearing chip with low contrast</h3>\n <!-- A <span> chip painting its own opaque background under direct text.\n SAFE_TAGS used to skip spans categorically; the styled-control\n exception must cover any text-bearing chip or badge. Mirrors a real\n bug: a SEV-2 chip whose white text lost a specificity fight and\n rendered muted brown on red at 1.2:1. -->\n <span class=\"chip-sev-low\" data-test=\"chip-sev\">SEV-2</span>\n\n <h3>Tailwind color anti-patterns</h3>\n <div class=\"bg-black text-white p-4 rounded card\" style=\"background: black; color: white;\">\n <p>bg-black — pure black bg</p>\n </div>\n <div class=\"bg-blue-500 text-gray-400 p-4 rounded card\" style=\"background: rgb(59, 130, 246);\">\n <p style=\"color: rgb(156, 163, 175);\">text-gray-400 on bg-blue-500 — gray on color</p>\n </div>\n <h3 class=\"text-purple-500 text-2xl font-bold\" style=\"color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;\">text-purple-500 heading</h3>\n <div class=\"bg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card\" style=\"background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;\">\n <p>Purple-to-indigo gradient</p>\n </div>\n\n <h3>currentcolor surface via var() token</h3>\n <!-- background-color: currentcolor with color: var(--fixture-bone).\n The surface is knowable (bone #e8e2d6), so the faint text on it is\n a real low-contrast finding, not an abstention. -->\n <div class=\"currentcolor-surface\" data-test=\"currentcolor-low\">\n <p class=\"currentcolor-low-text\">Faint warm gray on a bone currentcolor surface</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Tinted neutrals</h3>\n <div class=\"card\" style=\"background: rgb(26, 26, 30);\">\n <p style=\"color: rgb(240, 240, 245);\">Near-black bg, near-white text — tinted, not pure</p>\n </div>\n <div class=\"card\" style=\"background: rgb(250, 250, 252);\">\n <p style=\"color: rgb(20, 20, 25);\">Near-white bg, near-black text — good contrast, tinted</p>\n </div>\n\n <h3>Pure white surface is acceptable</h3>\n <div class=\"card\" style=\"background: #ffffff;\">\n <p style=\"color: #111827;\">Pure #fff background with dark text — acceptable contrast</p>\n </div>\n\n <h3>Good contrast</h3>\n <div class=\"card\" style=\"background: rgb(30, 64, 175);\">\n <p style=\"color: rgb(240, 240, 245);\">Near-white text on dark blue — high contrast, not pure white</p>\n </div>\n <div class=\"card\" style=\"background: rgb(16, 185, 129);\">\n <p style=\"color: rgb(5, 46, 32);\">Dark green text on green bg — same hue family</p>\n </div>\n\n <h3>Distinctive accents (not AI purple)</h3>\n <h3 style=\"color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;\">Red heading — not AI purple</h3>\n <h3 style=\"color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;\">Amber heading — distinctive</h3>\n\n <h3>Background-image url() ancestor (not low contrast)</h3>\n <!-- White text on transparent bg where ancestor has a background-image.\n The detector cannot determine the image color, so it must NOT assume\n white and report a false low-contrast finding. -->\n <div data-test=\"bg-image-ancestor\" style=\"background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;\">\n <p style=\"color: rgb(255, 255, 255); font-size: 16px;\">White text on image background — should not flag low-contrast</p>\n </div>\n\n <h3>Tailwind opacity-modified bg-black (not pure black)</h3>\n <!-- bg-black/N classes are NOT pure black — they apply an alpha modifier.\n The detector must not match these as pure-black-white. -->\n <div class=\"bg-black/3 p-4 rounded card\" data-test=\"bg-black-opacity\" style=\"background: rgba(0,0,0,0.03);\">\n <p>bg-black/3 — 3% opacity, not pure black</p>\n </div>\n <div class=\"hover:bg-black/5 p-4 rounded card\" data-test=\"bg-black-hover-opacity\" style=\"background: rgba(0,0,0,0);\">\n <p>hover:bg-black/5 — hover state, not pure black</p>\n </div>\n <div class=\"bg-black/50 p-4 rounded card\" data-test=\"bg-black-half-opacity\" style=\"background: rgba(0,0,0,0.5);\">\n <p style=\"color: white;\">bg-black/50 — 50% opacity, not pure black</p>\n </div>\n\n <h3>Inline &lt;a&gt; without own background (must remain skipped)</h3>\n <!-- A regular text link inside a paragraph. No own background. SAFE_TAGS\n must continue to skip these — flagging them would create noise on\n every text link on the web. The detector exception for styled\n buttons must not regress to flagging plain inline links. -->\n <p>Read more about this <a href=\"#\" class=\"inline-link-low\" data-test=\"inline-link\">here</a> if you want to learn more about the topic.</p>\n\n <h3>Styled &lt;a&gt; with good contrast (must not flag)</h3>\n <!-- A pill-style <a> styled correctly: cream text on near-black bg,\n high contrast. The detector exception for styled buttons must\n let the contrast check run, but this case must clearly pass. -->\n <a href=\"#\" class=\"good-pill-high\" data-test=\"good-pill\">High contrast pill</a>\n\n <h3>Chip with good contrast (must not flag)</h3>\n <span class=\"chip-ok-high\" data-test=\"chip-ok\">HEALTHY</span>\n\n <h3>Sub-9px decorative chip (below the font floor, must not flag)</h3>\n <span class=\"chip-sub9-low\" data-test=\"chip-sub9\">tick</span>\n\n <h3>background: none resets an earlier background-color</h3>\n <!-- The `background` shorthand resets background-color. `pre code\n { background: none }` after `code { background: <light> }` leaves\n the code text transparent over the dark panel; the cascade must not\n keep measuring it against the light surface it never paints. -->\n <div class=\"panel-reset\" data-test=\"panel-reset\">\n <pre><code data-test=\"code-reset\">light text over the dark panel, not the light code surface</code></pre>\n </div>\n\n <h3>Gradient-clipped text (issue #409 Case A — must not flag contrast)</h3>\n <!-- background-clip: text with a transparent fill: the gradient paints the\n glyphs, not a surface behind them. The inherited light `color` is\n never painted. The gradient-text pattern still flags; the backdrop\n contrast rules (low-contrast / gray-on-color) must not. -->\n <h1 class=\"ox-grad-text\" data-test=\"ox-grad-text\">Gradient Clipped Heading Text</h1>\n\n <h3>Faint accent-glow gradient (issue #409 Case B — must not flag contrast)</h3>\n <!-- A 9%-alpha teal glow over a dark section. Composited against the dark\n surface the stop is near-black, so the light and gray text on it are\n high-contrast. Treating the stop as opaque #34c0a8 was the false\n positive that flagged every text child. -->\n <div class=\"ox-dark-wrap\">\n <div class=\"ox-glow\" data-test=\"ox-glow\">\n <p>Light body copy on a faint accent glow that composites to near-black</p>\n <p class=\"muted\">Muted secondary line on the same faint glow area here</p>\n </div>\n </div>\n\n <h3>color-mix gradient stop: nested ingredient is not a surface</h3>\n <div class=\"mix-dark-wrap\">\n <div class=\"mix-glow\" data-test=\"mix-glow\">\n <p>Light copy on a faint mixed wash over a dark ground stays readable</p>\n </div>\n </div>\n\n <h3>currentcolor surface with good contrast</h3>\n <div class=\"currentcolor-surface\" data-test=\"currentcolor-good\">\n <p class=\"currentcolor-good-text\">Dark ink text on a bone currentcolor surface</p>\n </div>\n\n <h3>currentcolor surface with unresolvable token (must abstain)</h3>\n <!-- The var() token is undefined, so the surface genuinely cannot be\n read. The walk must abstain instead of guessing a background for\n the light text inside. -->\n <div class=\"currentcolor-unknown\" data-test=\"currentcolor-unknown\">\n <p>Light text on an unknowable currentcolor surface</p>\n </div>\n\n <h3>Emoji on light backgrounds</h3>\n <!-- Emojis render as multicolor glyphs regardless of CSS color, so the\n CSS color is irrelevant for contrast. These should NOT be flagged. -->\n <div class=\"card emoji-test\" data-test=\"emoji-light\" style=\"background: #ffe4e6;\">\n <p style=\"color: #ffe4e6; font-size: 24px;\">⚠️ 🚨 ✨ 🎨</p>\n </div>\n <div class=\"card emoji-test\" data-test=\"emoji-dark\" style=\"background: #1a1a1a;\">\n <p style=\"color: #1a1a1a; font-size: 24px;\">⚠️ 🚨 ✨ 🎨</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n /* Styled button color cases (declared via stylesheet so jsdom resolves them\n reliably — inline color doesn't always win over user-agent rules in jsdom\n for <a> and <button>). */\n .styled-pill-low { background-color: rgb(31, 26, 21); color: rgb(91, 79, 68); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .low-contrast-button { background-color: rgb(55, 65, 81); color: rgb(108, 114, 128); display: inline-block; padding: 10px 20px; border-radius: 8px; border: 0; font-size: 14px; }\n .good-pill-high { background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .inline-link-low { color: rgb(170, 170, 170); }\n /* Chip / badge cases: text-bearing spans painting their own background. */\n .chip-sev-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(92, 84, 73); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-ok-high { display: inline-block; background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-sub9-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(150, 60, 55); font-size: 8px; padding: 1px 4px; }\n .panel-reset { background: rgb(28, 30, 38); color: rgb(230, 232, 237); padding: 12px; }\n .panel-reset code { background: rgb(246, 242, 244); border-radius: 3px; padding: 1px 4px; }\n .panel-reset pre code { background: none; }\n /* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill\n (text-fill-color: transparent), not a backdrop. The inherited `color`\n (#e8e6e3) is never painted, so measuring it against the element's own\n gradient stops (#6d8cff / #a78bfa) is a false positive. */\n .ox-grad-text { background: linear-gradient(135deg, #6d8cff 0%, #a78bfa 50%, #6d8cff 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: #e8e6e3; font-size: 40px; font-weight: 800; }\n /* issue #409 Case B: a 9%-alpha accent glow stop over a dark surface. The\n stop composites to ~#121f1f, not the full-opacity #34c0a8, so text stays\n high-contrast. The dark wrapper supplies the surface beneath the glow. */\n .ox-dark-wrap { background: #0f0f11; padding: 16px; }\n .ox-glow { background: linear-gradient(160deg, rgba(52,192,168,0.09) 0%, #141419 65%); padding: 20px; }\n .ox-glow p { color: #e8e6e3; font-size: 18px; }\n .ox-glow .muted { color: #8e8c89; font-size: 16px; }\n /* Gradient stop written as color-mix with a bright nested ingredient.\n The stop paints as a 16% wash of the light oklch color (composited\n over the dark wrap it is near-black); the nested oklch(90% ...) is an\n INGREDIENT, never painted. A parser that re-extracts nested tokens\n appends it as a phantom opaque light stop and the worst-case ratio\n then flags the light text at ~1:1 against a color nobody sees. */\n .mix-dark-wrap { background: #0f0f11; padding: 16px; }\n .mix-glow { background: linear-gradient(160deg, color-mix(in oklab, oklch(90% 0.02 95) 16%, transparent) 0%, #141419 65%); padding: 20px; }\n .mix-glow p { color: #ded9cf; font-size: 16px; }\n /* currentcolor surface: background-color paints with the element's own\n text color, which is itself a var() token here. jsdom hands both\n through verbatim, so the walk must resolve the token via the\n custom-prop map instead of abstaining on a knowable surface. */\n :root { --fixture-bone: #e8e2d6; }\n .currentcolor-surface { background-color: currentcolor; color: var(--fixture-bone); padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .currentcolor-low-text { color: #cfc9bd; font-size: 14px; }\n .currentcolor-good-text { color: #3a352c; font-size: 14px; }\n /* Same shape but the token does not exist: the surface truly cannot be\n read, so the walk must abstain rather than guess. */\n .currentcolor-unknown { background-color: currentcolor; color: var(--fixture-undefined-token); padding: 14px 16px; border-radius: 10px; }\n .currentcolor-unknown p { color: #efe9dd; font-size: 14px; }\n \nstyle=\"background: #000000; color: white;\"\nstyle=\"background: rgb(59, 130, 246);\"\nstyle=\"color: rgb(150, 150, 150);\"\nstyle=\"background: rgb(16, 185, 129);\"\nstyle=\"color: rgb(180, 180, 180);\"\nstyle=\"background: white;\"\nstyle=\"color: rgb(200, 200, 200);\"\nstyle=\"background: rgb(30, 30, 30);\"\nstyle=\"color: rgb(80, 80, 80);\"\nstyle=\"background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;\"\nstyle=\"color: #808080; font-size: 32px; font-weight: 800; margin: 0;\"\nstyle=\"color: #666666; font-size: 14px;\"\nstyle=\"background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;\"\nstyle=\"color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;\"\nstyle=\"background: black; color: white;\"\nstyle=\"background: rgb(59, 130, 246);\"\nstyle=\"color: rgb(156, 163, 175);\"\nstyle=\"color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;\"\nstyle=\"background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;\"\nstyle=\"background: rgb(26, 26, 30);\"\nstyle=\"color: rgb(240, 240, 245);\"\nstyle=\"background: rgb(250, 250, 252);\"\nstyle=\"color: rgb(20, 20, 25);\"\nstyle=\"background: #ffffff;\"\nstyle=\"color: #111827;\"\nstyle=\"background: rgb(30, 64, 175);\"\nstyle=\"color: rgb(240, 240, 245);\"\nstyle=\"background: rgb(16, 185, 129);\"\nstyle=\"color: rgb(5, 46, 32);\"\nstyle=\"color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;\"\nstyle=\"color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;\"\nstyle=\"background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;\"\nstyle=\"color: rgb(255, 255, 255); font-size: 16px;\"\nstyle=\"background: rgba(0,0,0,0.03);\"\nstyle=\"background: rgba(0,0,0,0);\"\nstyle=\"background: rgba(0,0,0,0.5);\"\nstyle=\"color: white;\"\nstyle=\"background: #ffe4e6;\"\nstyle=\"color: #ffe4e6; font-size: 24px;\"\nstyle=\"background: #1a1a1a;\"\nstyle=\"color: #1a1a1a; font-size: 24px;\"","classText":"grid\ncol\ncard\ncard\ncard\ncard\ncard\ncard\nstyled-pill-low\nlow-contrast-button\nchip-sev-low\nbg-black text-white p-4 rounded card\nbg-blue-500 text-gray-400 p-4 rounded card\ntext-purple-500 text-2xl font-bold\nbg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card\ncurrentcolor-surface\ncurrentcolor-low-text\ncol\ncard\ncard\ncard\ncard\ncard\nbg-black/3 p-4 rounded card\nhover:bg-black/5 p-4 rounded card\nbg-black/50 p-4 rounded card\ninline-link-low\ngood-pill-high\nchip-ok-high\nchip-sub9-low\npanel-reset\nox-grad-text\nox-dark-wrap\nox-glow\nmuted\nmix-dark-wrap\nmix-glow\ncurrentcolor-surface\ncurrentcolor-good-text\ncurrentcolor-unknown\ncard emoji-test\ncard emoji-test"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":".currentcolor-unknown p"},{"id":"gradient-text","snippet":"background-clip: text + gradient","selector":"/* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill (text-fill-color: transparent), not a backdrop. The inherited `color` (#e8e6e3) is never painted, so measuring it against the element's own gradient stops (#6d8cff / #a78bfa) is a false positive. */ .ox-grad-text"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Cramped Padding — Should Flag vs Should Pass</title>\n <style>\n /* Comprehensive fixture for the cramped-padding rule.\n Both columns include a wide variety of cases so we can verify the\n rule scales sensibly with font-size and treats the two axes\n independently (vertical can be tighter because line-height already\n provides built-in breathing room; horizontal has no equivalent). */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; padding: 14px 16px; background: white; border: 1px solid #e2e8f0; border-radius: 10px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; }\n\n /* ── FLAG: cramped body in cards ── */\n .flag-card-4 { padding: 4px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; }\n .flag-card-2 { padding: 2px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n .flag-card-bg4 { padding: 4px; background: #fef3c7; border-radius: 6px; font-size: 16px; }\n\n /* ── FLAG: asymmetric cramping ── */\n .flag-asym-v { padding: 1px 16px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n .flag-asym-h { padding: 12px 4px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n\n /* ── FLAG: cramped large text (currently MISSED by the 8px floor) ── */\n .flag-heading-tight { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 24px; font-weight: 600; line-height: 1.2; }\n .flag-hero-tight { padding: 6px 16px; background: #f1f5f9; border-radius: 6px; font-size: 32px; font-weight: 700; line-height: 1.2; }\n\n /* ── FLAG: cramped multi-line code block ── */\n .flag-pre-tight { padding: 2px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n\n /* ── PASS: small inline pills (the .detection-cmd category) ── */\n .pass-cmd {\n display: inline-block;\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 13px;\n line-height: 1.625;\n color: #0f172a;\n background: #f5f5f7;\n padding: 6px 14px;\n border-radius: 6px;\n white-space: nowrap;\n }\n .pass-cmd::before { content: '➜ '; color: #ec4899; }\n .pass-badge {\n display: inline-block;\n font-size: 11px;\n line-height: 1.6;\n padding: 4px 8px;\n background: #dbeafe;\n color: #1e40af;\n border-radius: 999px;\n font-weight: 600;\n }\n .pass-chip {\n display: inline-block;\n font-size: 12px;\n line-height: 1.6;\n padding: 4px 10px;\n background: #fef3c7;\n color: #92400e;\n border-radius: 4px;\n font-weight: 500;\n }\n .pass-inline-code-chip {\n display: inline-block;\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 12px;\n line-height: 2;\n padding: 0.3em 0.5em;\n background: #eef2f7;\n color: #0f172a;\n border-radius: 4px;\n }\n\n /* ── PASS: cards at current standards ── */\n .pass-card-min { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 16px; line-height: 1.6; }\n .pass-card-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 14px; line-height: 1.6; }\n .pass-card-large { padding: 12px; background: #f1f5f9; border-radius: 8px; font-size: 18px; line-height: 1.6; }\n\n /* ── PASS: comfortable code blocks ── */\n .pass-pre-min { padding: 8px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n .pass-pre-good { padding: 12px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n\n /* ── PASS: interactive elements ── */\n .pass-button-std { padding: 8px 16px; background: #3b82f6; color: white; border: 0; border-radius: 6px; font-size: 14px; cursor: pointer; }\n .pass-input-std { padding: 8px 12px; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 16px; width: 100%; box-sizing: border-box; }\n\n /* ── PASS: big text with proportional padding ── */\n .pass-heading-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 24px; font-weight: 600; line-height: 1.2; }\n .pass-hero-good { padding: 20px 32px; background: #f1f5f9; border-radius: 8px; font-size: 32px; font-weight: 700; line-height: 1.2; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Cramped body in cards</h3>\n <div class=\"case\">\n <span class=\"case-label\">14px body, 4px padding all sides</span>\n <div class=\"flag-card-4\">This text is crammed against the border with only 4px padding. It feels claustrophobic and hard to read because the eye has nowhere to rest.</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">14px body, 2px padding all sides</span>\n <div class=\"flag-card-2\">Two pixels of padding is essentially nothing — body text needs room to breathe inside its container, especially across multiple lines.</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">16px body, 4px padding, background only</span>\n <div class=\"flag-card-bg4\">A colored background containment with only 4px padding around 16px body text. The colored edge feels like it's pressing in.</div>\n </div>\n\n <h3>Asymmetric cramping</h3>\n <div class=\"case\">\n <span class=\"case-label\">1px vertical / 16px horizontal, 14px body</span>\n <div class=\"flag-asym-v\">Generous horizontal padding doesn't save you when the vertical padding is just one pixel. Lines stack on top of each other against the container edge.</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">12px vertical / 4px horizontal, 14px body</span>\n <div class=\"flag-asym-h\">Comfortable vertical padding can't fix horizontal text running right up against the side edges. Words feel like they're falling off the container.</div>\n </div>\n\n <h3>Currently missed: cramped large text</h3>\n <div class=\"case\">\n <span class=\"case-label\">24px heading, 8px padding (passes the 8px floor but is too tight for the text size)</span>\n <div class=\"flag-heading-tight\">Tight Heading Container Label</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">32px hero, 6px vertical / 16px horizontal padding</span>\n <div class=\"flag-hero-tight\">Hero Section With Tight Vertical Padding</div>\n </div>\n\n <h3>Cramped multi-line code block</h3>\n <div class=\"case\">\n <span class=\"case-label\">&lt;pre&gt;, 2px padding, 14px monospace</span>\n <pre class=\"flag-pre-tight\">function example() {\n const value = \"cramped\";\n return value;\n}</pre>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Small inline pills</h3>\n <div class=\"case\">\n <span class=\"case-label\">.detection-cmd replica: 13px font, 6px / 14px padding (the homepage case)</span>\n <code class=\"pass-cmd\">npx impeccable detect src/</code>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">small badge: 11px font, 4px / 8px padding</span>\n <span class=\"pass-badge\">FEATURED ITEM BADGE</span>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">tag chip: 12px font, 4px / 10px padding</span>\n <span class=\"pass-chip\">design system tag chip</span>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">long inline code chip: compact inline code should not be treated like a padded card</span>\n <code class=\"pass-inline-code-chip\">npx impeccable check</code>\n </div>\n\n <h3>Cards at current standards</h3>\n <div class=\"case\">\n <span class=\"case-label\">16px body, 8px padding (the original 8px floor)</span>\n <div class=\"pass-card-min\">Standard card with 8px padding around 16px body text. Comfortable and within current conventions.</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">14px body, 16px padding (generous)</span>\n <div class=\"pass-card-good\">Generous 16px padding around 14px body text. The eye has plenty of room from the container edge.</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">18px body, 12px padding (proportional)</span>\n <div class=\"pass-card-large\">Larger body text gets proportionally more padding. 12px around 18px text feels balanced.</div>\n </div>\n\n <h3>Comfortable code blocks</h3>\n <div class=\"case\">\n <span class=\"case-label\">multi-line &lt;pre&gt;, 8px padding, 14px font</span>\n <pre class=\"pass-pre-min\">function example() {\n const value = \"ok\";\n return value;\n}</pre>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">multi-line &lt;pre&gt;, 12px padding, 14px font</span>\n <pre class=\"pass-pre-good\">function example() {\n const value = \"comfortable\";\n return value;\n}</pre>\n </div>\n\n <h3>Interactive elements</h3>\n <div class=\"case\">\n <span class=\"case-label\">button: 14px font, 8px / 16px padding</span>\n <button class=\"pass-button-std\">Standard sized button</button>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">form input: 16px font, 8px / 12px padding</span>\n <input type=\"text\" class=\"pass-input-std\" value=\"Standard input with adequate padding\">\n </div>\n\n <h3>Big text with proportional padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">24px heading, 16px padding</span>\n <div class=\"pass-heading-good\">Heading With Room To Breathe</div>\n </div>\n <div class=\"case\">\n <span class=\"case-label\">32px hero, 20px / 32px padding</span>\n <div class=\"pass-hero-good\">Hero With Big Padding</div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Comprehensive fixture for the cramped-padding rule.\n Both columns include a wide variety of cases so we can verify the\n rule scales sensibly with font-size and treats the two axes\n independently (vertical can be tighter because line-height already\n provides built-in breathing room; horizontal has no equivalent). */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; padding: 14px 16px; background: white; border: 1px solid #e2e8f0; border-radius: 10px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; }\n\n /* ── FLAG: cramped body in cards ── */\n .flag-card-4 { padding: 4px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; }\n .flag-card-2 { padding: 2px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n .flag-card-bg4 { padding: 4px; background: #fef3c7; border-radius: 6px; font-size: 16px; }\n\n /* ── FLAG: asymmetric cramping ── */\n .flag-asym-v { padding: 1px 16px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n .flag-asym-h { padding: 12px 4px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }\n\n /* ── FLAG: cramped large text (currently MISSED by the 8px floor) ── */\n .flag-heading-tight { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 24px; font-weight: 600; line-height: 1.2; }\n .flag-hero-tight { padding: 6px 16px; background: #f1f5f9; border-radius: 6px; font-size: 32px; font-weight: 700; line-height: 1.2; }\n\n /* ── FLAG: cramped multi-line code block ── */\n .flag-pre-tight { padding: 2px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n\n /* ── PASS: small inline pills (the .detection-cmd category) ── */\n .pass-cmd {\n display: inline-block;\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 13px;\n line-height: 1.625;\n color: #0f172a;\n background: #f5f5f7;\n padding: 6px 14px;\n border-radius: 6px;\n white-space: nowrap;\n }\n .pass-cmd::before { content: '➜ '; color: #ec4899; }\n .pass-badge {\n display: inline-block;\n font-size: 11px;\n line-height: 1.6;\n padding: 4px 8px;\n background: #dbeafe;\n color: #1e40af;\n border-radius: 999px;\n font-weight: 600;\n }\n .pass-chip {\n display: inline-block;\n font-size: 12px;\n line-height: 1.6;\n padding: 4px 10px;\n background: #fef3c7;\n color: #92400e;\n border-radius: 4px;\n font-weight: 500;\n }\n .pass-inline-code-chip {\n display: inline-block;\n font-family: ui-monospace, SFMono-Regular, monospace;\n font-size: 12px;\n line-height: 2;\n padding: 0.3em 0.5em;\n background: #eef2f7;\n color: #0f172a;\n border-radius: 4px;\n }\n\n /* ── PASS: cards at current standards ── */\n .pass-card-min { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 16px; line-height: 1.6; }\n .pass-card-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 14px; line-height: 1.6; }\n .pass-card-large { padding: 12px; background: #f1f5f9; border-radius: 8px; font-size: 18px; line-height: 1.6; }\n\n /* ── PASS: comfortable code blocks ── */\n .pass-pre-min { padding: 8px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n .pass-pre-good { padding: 12px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }\n\n /* ── PASS: interactive elements ── */\n .pass-button-std { padding: 8px 16px; background: #3b82f6; color: white; border: 0; border-radius: 6px; font-size: 14px; cursor: pointer; }\n .pass-input-std { padding: 8px 12px; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 16px; width: 100%; box-sizing: border-box; }\n\n /* ── PASS: big text with proportional padding ── */\n .pass-heading-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 24px; font-weight: 600; line-height: 1.2; }\n .pass-hero-good { padding: 20px 32px; background: #f1f5f9; border-radius: 8px; font-size: 32px; font-weight: 700; line-height: 1.2; }\n ","classText":"grid\ncol\ncase\ncase-label\nflag-card-4\ncase\ncase-label\nflag-card-2\ncase\ncase-label\nflag-card-bg4\ncase\ncase-label\nflag-asym-v\ncase\ncase-label\nflag-asym-h\ncase\ncase-label\nflag-heading-tight\ncase\ncase-label\nflag-hero-tight\ncase\ncase-label\nflag-pre-tight\ncol\ncase\ncase-label\npass-cmd\ncase\ncase-label\npass-badge\ncase\ncase-label\npass-chip\ncase\ncase-label\npass-inline-code-chip\ncase\ncase-label\npass-card-min\ncase\ncase-label\npass-card-good\ncase\ncase-label\npass-card-large\ncase\ncase-label\npass-pre-min\ncase\ncase-label\npass-pre-good\ncase\ncase-label\npass-button-std\ncase\ncase-label\npass-input-std\ncase\ncase-label\npass-heading-good\ncase\ncase-label\npass-hero-good"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Cream palette (Tailwind) fixture</title>\n</head>\n<body class=\"bg-amber-50 text-stone-800 font-serif\">\n <h1>A warm cream surface via Tailwind</h1>\n <p>The background comes from a utility class (bg-amber-50), not inline CSS, so the static engine never resolves it to a computed color. The class-list fallback should still catch it.</p>\n</body>\n</html>\n",{"styleText":"","classText":"bg-amber-50 text-stone-800 font-serif"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Cream palette fixture</title>\n <style>\n body { background: #f5efe2; color: #2a2622; font-family: Georgia, serif; margin: 0; padding: 40px; }\n h1 { font-size: 40px; margin: 0 0 16px; }\n p { max-width: 60ch; line-height: 1.6; }\n </style>\n</head>\n<body>\n <h1>A warm cream surface</h1>\n <p>The whole page sits on the default tasteful off-white that every generated landing page reaches for. The background is the tell, regardless of what sits on top of it.</p>\n</body>\n</html>\n",{"styleText":"\n body { background: #f5efe2; color: #2a2622; font-family: Georgia, serif; margin: 0; padding: 40px; }\n h1 { font-size: 40px; margin: 0 0 16px; }\n p { max-width: 60ch; line-height: 1.6; }\n ","classText":""}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Companion to css-in-prose-should-pass.html: the same patterns applied as\n real styling. The scoped-corpus scan must still catch every carrier a page\n can style itself with — a <style> rule, an inline style=\"\" attribute, and\n Tailwind utility classes.\n-->\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Real gradient-text styling</title>\n<style>\n body { font-family: Georgia, serif; margin: 0; padding: 40px 24px; background: #fdfcf9; color: #1f2933; }\n /* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */\n .gradient-heading {\n background: linear-gradient(135deg, #7c3aed, #a855f7);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n font-size: 44px;\n }\n</style>\n</head>\n<body>\n<h1 class=\"gradient-heading\">Ship Faster With Us</h1>\n\n<!-- SHOULD FLAG (gradient-text): the same construction inline. -->\n<h2 style=\"background: linear-gradient(90deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 28px;\">Inline gradient heading</h2>\n\n<!-- SHOULD FLAG (gradient-text, Tailwind): bg-clip-text + bg-gradient-to- classes. -->\n<h2 class=\"bg-clip-text bg-gradient-to-r from-purple-500 to-pink-500 text-transparent\">Tailwind gradient heading</h2>\n\n<p>Body copy long enough to make this a real page for the scanners to read.</p>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; padding: 40px 24px; background: #fdfcf9; color: #1f2933; }\n /* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */\n .gradient-heading {\n background: linear-gradient(135deg, #7c3aed, #a855f7);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n font-size: 44px;\n }\n\nstyle=\"background: linear-gradient(90deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 28px;\"","classText":"gradient-heading\nbg-clip-text bg-gradient-to-r from-purple-500 to-pink-500 text-transparent"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":"/* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */ .gradient-heading"},{"id":"gradient-text","snippet":"background-clip: text + gradient","selector":"/* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */ .gradient-heading"},{"id":"gradient-text","snippet":"bg-clip-text + bg-gradient (Tailwind)"}]}
{"args":["<!DOCTYPE html>\n<!--\n Regression fixture: CSS-property patterns appearing in prose, code samples,\n and comments are documentation ABOUT css, not styling. The page-level\n pattern checks must scan only real style carriers (<style> blocks, style=\"\"\n attribute values, linked stylesheets, class=\"\" attribute values), never\n rendered text. Live false positive that motivated this fixture: the\n impeccable.style changelog line\n \"<code>background-clip: text</code> gradients stop tripping contrast\"\n tripped gradient-text on the raw-source scan, and the purple hex codes in\n its rule documentation shared the same exposure via ai-color-palette.\n\n Every case in this file SHOULD PASS: nothing here styles the page with the\n documented patterns.\n-->\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Docs about CSS anti-patterns</title>\n<style>\n body { font-family: Georgia, 'Iowan Old Style', serif; margin: 0; padding: 32px 24px 40px; color: #1f2933; background: #fdfcf9; }\n h1 { font-size: 34px; margin: 0 0 12px; }\n h2 { font-size: 20px; margin: 28px 0 8px; }\n p { font-size: 16px; max-width: 640px; }\n pre { background: #f1efe9; padding: 16px; overflow-x: auto; font-size: 13px; }\n code { font-family: 'SF Mono', ui-monospace, monospace; }\n</style>\n</head>\n<body>\n<h1>Changelog</h1>\n\n<!-- SHOULD PASS (gradient-text): the exact changelog shape — inline <code>\n naming the property with \"gradient\" prose within the context window. -->\n<h2>Detector</h2>\n<p><code>background-clip: text</code> gradients stop tripping the contrast rules when the glyph fill is transparent.</p>\n\n<!-- SHOULD PASS (gradient-text + ai-color-palette): a <pre> CSS sample\n documenting the pattern, purple hexes and all. -->\n<h2>What the rule catches</h2>\n<pre><code>.hero-title {\n background: linear-gradient(135deg, #667eea, #764ba2);\n -webkit-background-clip: text;\n background-clip: text;\n color: #7c3aed;\n}</code></pre>\n\n<!-- SHOULD PASS (gradient-text, Tailwind form): prose naming the utility\n classes without applying them. -->\n<p>The Tailwind form pairs bg-clip-text with bg-gradient-to-r on one element; the two utilities together are the tell.</p>\n\n<!-- SHOULD PASS (gradient-text): an HTML comment carrying the pattern.\n background-clip: text; with a linear-gradient(90deg, #7c3aed, #a855f7)\n fill is the construction the rule looks for. -->\n\n<!-- SHOULD PASS (pulsing-dot): a <pre> sample of the decorative live dot. -->\n<h2>Pulsing dots</h2>\n<pre><code>.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n animation: pulse 2s ease-in-out infinite;\n}</code></pre>\n\n<!-- SHOULD PASS (side-tab): a <pre> sample of the pseudo-element stripe. -->\n<h2>Side stripes</h2>\n<pre><code>.card::before {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 4px;\n background: #3b82f6;\n}</code></pre>\n\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, 'Iowan Old Style', serif; margin: 0; padding: 32px 24px 40px; color: #1f2933; background: #fdfcf9; }\n h1 { font-size: 34px; margin: 0 0 12px; }\n h2 { font-size: 20px; margin: 28px 0 8px; }\n p { font-size: 16px; max-width: 640px; }\n pre { background: #f1efe9; padding: 16px; overflow-x: auto; font-size: 13px; }\n code { font-family: 'SF Mono', ui-monospace, monospace; }\n","classText":""}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Fixture: gradient body ground, oklch stops (low-contrast)</title>\n<style>\n /* The impeccable.style false-positive class: the page ground is a gradient\n set via `background:` shorthand, so the body's computed backgroundColor\n is transparent and the ground exists only as a backgroundImage whose\n stops serialize in oklch. The detector must measure text against those\n stops (composited under any translucent layers), never against an\n assumed white body. */\n body {\n margin: 0;\n padding: 48px;\n background: linear-gradient(180deg, oklch(7% 0.006 95), oklch(4% 0.004 95));\n font-family: Arial, sans-serif;\n }\n body > * { margin: 0 0 32px; }\n\n /* ── Should flag ─────────────────────────────────────────────────── */\n .flag-muted-direct {\n width: 560px;\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .flag-muted-nested-wrapper { width: 560px; }\n .flag-muted-nested {\n color: oklch(32% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n .frosted-light {\n width: 480px;\n padding: 24px;\n background: rgba(250, 249, 246, 0.88);\n border-radius: 4px;\n }\n .flag-light-on-frosted {\n color: oklch(88% 0 0);\n font-size: 15px;\n line-height: 1.6;\n margin: 0;\n }\n\n /* ── Should pass ─────────────────────────────────────────────────── */\n .pass-hero-title {\n width: 640px;\n color: oklch(91% 0 0);\n font-size: 48px;\n }\n .pass-hero-body {\n width: 560px;\n color: oklch(88% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .pass-on-raised {\n width: 420px;\n padding: 24px;\n background: oklch(11% 0.006 95);\n color: oklch(88% 0 0);\n font-size: 15px;\n }\n .pass-dark-on-frosted {\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n margin: 0 0 12px;\n }\n .pass-hex-gradient {\n width: 520px;\n padding: 24px;\n background-image: linear-gradient(180deg, #14120f, #0a0908);\n color: oklch(90% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-image-backed {\n width: 460px;\n padding: 18px 22px;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2)), url(\"/fixtures/antipatterns/no-such-texture.png\") center / cover;\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-photo-panel {\n width: 460px;\n padding: 18px 22px;\n background-image: url(\"/fixtures/antipatterns/no-such-photo.jpg\");\n color: oklch(20% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-hidden-scene {\n opacity: 0;\n width: 560px;\n }\n .pass-hidden-scene p {\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n</style>\n</head>\n<body>\n <h1 class=\"pass-hero-title\">Light display text on the lacquer gradient</h1>\n <p class=\"pass-hero-body\">This paragraph sits directly on the body's dark gradient ground. Its near-white ink clears WCAG AA against both gradient stops by a wide margin, so a detector that measures the real ground must not flag it. A detector that assumes a white body flags it at about 1.3:1.</p>\n <div class=\"pass-on-raised\">Light text on a raised solid oklch surface between the text and the body gradient still resolves through the normal opaque-ancestor walk.</div>\n <div class=\"frosted-light\">\n <p class=\"pass-dark-on-frosted\">Dark ink on a translucent light wash over the dark gradient. The wash composites to a light surface, so this passes; measuring raw gradient stops would wrongly flag it.</p>\n <p class=\"flag-light-on-frosted\">Light ink on the same translucent light wash genuinely fails once the wash is composited over the stops; raw stops would wrongly pass it.</p>\n </div>\n <p class=\"flag-muted-direct\">This muted dark-gray ink genuinely fails contrast against both stops of the dark gradient ground, which proves the gradient stops are being measured instead of the checks silently skipping.</p>\n <div class=\"flag-muted-nested-wrapper\">\n <p class=\"flag-muted-nested\">Muted ink reached through a transparent wrapper still resolves to the body gradient and still fails against its stops.</p>\n </div>\n <div class=\"pass-hex-gradient\">A non-body gradient with legacy hex stops keeps working through the original rgb/hex parser, and light text on it passes.</div>\n <div class=\"pass-image-backed\">Dark ink on a translucent wash over a texture image. The image pixels are unknowable to the analytic walk, so contrast must skip rather than composite the wash over the wrong base.</div>\n <div class=\"pass-photo-panel\">Dark ink on a photo-backed panel sitting over the dark body gradient. Walking past the photo to the gradient stops would wrongly flag this; the unmeasurable image layer must end the walk.</div>\n <div class=\"pass-hidden-scene\"><p>Muted ink inside an opacity-zero scene deck. Nobody sees this state, so the contrast checks must not measure it.</p></div>\n</body>\n</html>\n",{"styleText":"\n /* The impeccable.style false-positive class: the page ground is a gradient\n set via `background:` shorthand, so the body's computed backgroundColor\n is transparent and the ground exists only as a backgroundImage whose\n stops serialize in oklch. The detector must measure text against those\n stops (composited under any translucent layers), never against an\n assumed white body. */\n body {\n margin: 0;\n padding: 48px;\n background: linear-gradient(180deg, oklch(7% 0.006 95), oklch(4% 0.004 95));\n font-family: Arial, sans-serif;\n }\n body > * { margin: 0 0 32px; }\n\n /* ── Should flag ─────────────────────────────────────────────────── */\n .flag-muted-direct {\n width: 560px;\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .flag-muted-nested-wrapper { width: 560px; }\n .flag-muted-nested {\n color: oklch(32% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n .frosted-light {\n width: 480px;\n padding: 24px;\n background: rgba(250, 249, 246, 0.88);\n border-radius: 4px;\n }\n .flag-light-on-frosted {\n color: oklch(88% 0 0);\n font-size: 15px;\n line-height: 1.6;\n margin: 0;\n }\n\n /* ── Should pass ─────────────────────────────────────────────────── */\n .pass-hero-title {\n width: 640px;\n color: oklch(91% 0 0);\n font-size: 48px;\n }\n .pass-hero-body {\n width: 560px;\n color: oklch(88% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .pass-on-raised {\n width: 420px;\n padding: 24px;\n background: oklch(11% 0.006 95);\n color: oklch(88% 0 0);\n font-size: 15px;\n }\n .pass-dark-on-frosted {\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n margin: 0 0 12px;\n }\n .pass-hex-gradient {\n width: 520px;\n padding: 24px;\n background-image: linear-gradient(180deg, #14120f, #0a0908);\n color: oklch(90% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-image-backed {\n width: 460px;\n padding: 18px 22px;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2)), url(\"/fixtures/antipatterns/no-such-texture.png\") center / cover;\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-photo-panel {\n width: 460px;\n padding: 18px 22px;\n background-image: url(\"/fixtures/antipatterns/no-such-photo.jpg\");\n color: oklch(20% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-hidden-scene {\n opacity: 0;\n width: 560px;\n }\n .pass-hidden-scene p {\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n","classText":"pass-hero-title\npass-hero-body\npass-on-raised\nfrosted-light\npass-dark-on-frosted\nflag-light-on-frosted\nflag-muted-direct\nflag-muted-nested-wrapper\nflag-muted-nested\npass-hex-gradient\npass-image-backed\npass-photo-panel\npass-hidden-scene"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Dark theme, modern color syntax</title>\n<style>\n /* A lacquer-black ground painted as a gradient in oklch, the shape that\n floods dark themes with \"on #ffffff\" low-contrast false positives when\n the detector cannot read the ground and assumes white. */\n body {\n margin: 0;\n padding: 24px;\n background: linear-gradient(oklch(0.07 0.006 95), oklch(0.11 0.008 95));\n font-family: Georgia, serif;\n font-size: 15px;\n line-height: 1.6;\n }\n\n section {\n padding: 24px;\n margin-bottom: 24px;\n width: 640px;\n }\n\n p { margin: 0 0 12px; font-size: 15px; }\n\n /* Readable grounds, each written in a color syntax a real browser keeps\n verbatim in getComputedStyle output. */\n .panel-srgb { background-color: color(srgb 0.1 0.11 0.12); }\n .panel-mix { background-color: color-mix(in srgb, white 6%, #0b0b0d); }\n .panel-p3 { background-color: color(display-p3 0.07 0.07 0.08); }\n .panel-lch { background-color: lch(18 4 60); }\n .panel-light { background-color: color(srgb 0.98 0.97 0.95); }\n\n /* A color space the detector does not model. Nested inside the light\n panel so a detector that walks PAST the unreadable layer would measure\n light text against the light section and invent a finding. */\n .panel-unreadable { background-color: color(rec2020 0.05 0.05 0.05); padding: 20px; }\n\n /* An image layer stacked ABOVE a loud gradient. The image is the visible\n surface and its pixels are unreadable; a detector that falls back to\n gradient stops here measures a layer the visitor never sees. */\n .panel-image-over-gradient { background-image: url(\"opaque-panel.png\"), linear-gradient(#ff0000, #0000ff); }\n\n /* Keywords a real browser resolves before getComputedStyle output but a\n partial cascade hands through verbatim: inherit takes the parent's\n ground, currentcolor paints with the element's own text color. Both are\n knowable surfaces; abstaining on them would hide real findings. */\n .panel-inherit { background-color: inherit; padding: 20px; }\n .panel-current { background-color: currentcolor; color: color(srgb 0.94 0.93 0.91); }\n .text-pale-2 { color: color(srgb 0.78 0.77 0.75); }\n\n .text-light { color: oklch(0.92 0.01 90); }\n .text-dark { color: color(srgb 0.1 0.11 0.12); }\n\n .text-muted-ground { color: oklch(0.32 0.01 90); }\n .text-dim-srgb { color: color(srgb 0.28 0.28 0.3); }\n .text-dim-p3 { color: color(display-p3 0.35 0.35 0.36); }\n .text-dim-lch { color: lch(35 3 60); }\n .text-pale-light { color: color(srgb 0.75 0.74 0.72); }\n</style>\n</head>\n<body>\n\n<h1 class=\"text-light\">Should flag</h1>\n\n<section>\n <p class=\"text-muted-ground\">Flag Muted On Oklch Ground: charcoal body copy sitting straight on the lacquer gradient.</p>\n</section>\n\n<section class=\"panel-srgb\">\n <p class=\"text-dim-srgb\">Flag Dim On Srgb Panel: slate copy on a near-black srgb surface.</p>\n</section>\n\n<section class=\"panel-p3\">\n <p class=\"text-dim-p3\">Flag Dim On Display P3 Panel: slate copy on a near-black display-p3 surface.</p>\n</section>\n\n<section class=\"panel-lch\">\n <p class=\"text-dim-lch\">Flag Dim On Lch Panel: warm gray copy on a warm near-black lch surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <p class=\"text-pale-light\">Flag Pale On Light Panel: bone copy on a bone-white srgb surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <div class=\"panel-inherit\">\n <p class=\"text-pale-2\">Flag Pale On Inherited Light Panel: the ground is inherited from the light section, so the walk must resolve it rather than abstain.</p>\n </div>\n</section>\n\n<section class=\"panel-current\">\n <p class=\"text-pale-light\">Flag Pale On Currentcolor Panel: the ground is the panel's own bone text color, so the keyword must resolve rather than abstain.</p>\n</section>\n\n<h1 class=\"text-light\">Should pass</h1>\n\n<section>\n <p class=\"text-light\">Pass Light Text On Oklch Gradient Ground: warm ivory copy on the lacquer gradient, roughly 15:1.</p>\n</section>\n\n<section class=\"panel-srgb\">\n <p class=\"text-light\">Pass Light Text On Srgb Panel: warm ivory copy on the near-black srgb surface.</p>\n</section>\n\n<section class=\"panel-mix\">\n <p class=\"text-light\">Pass Light Text On Color Mix Panel: warm ivory copy on a surface a browser reports as color(srgb ...).</p>\n</section>\n\n<section class=\"panel-p3\">\n <p class=\"text-light\">Pass Light Text On Display P3 Panel: warm ivory copy on the near-black display-p3 surface.</p>\n</section>\n\n<section class=\"panel-lch\">\n <p class=\"text-light\">Pass Light Text On Lch Panel: warm ivory copy on the warm near-black lch surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <p class=\"text-dark\">Pass Dark Text On Light Panel: ink copy on the bone-white srgb surface.</p>\n <div class=\"panel-unreadable\">\n <p class=\"text-light\">Pass Unreadable Panel Inside Light Section: the ground is a color space the detector cannot read, so it must abstain instead of measuring against the light section or against white.</p>\n </div>\n</section>\n\n<section class=\"panel-image-over-gradient\">\n <p class=\"text-muted-ground\">Pass Image Over Gradient: the visible ground is an image whose pixels the engine cannot read, so it must abstain instead of measuring the loud gradient hidden beneath it.</p>\n</section>\n\n</body>\n</html>\n",{"styleText":"\n /* A lacquer-black ground painted as a gradient in oklch, the shape that\n floods dark themes with \"on #ffffff\" low-contrast false positives when\n the detector cannot read the ground and assumes white. */\n body {\n margin: 0;\n padding: 24px;\n background: linear-gradient(oklch(0.07 0.006 95), oklch(0.11 0.008 95));\n font-family: Georgia, serif;\n font-size: 15px;\n line-height: 1.6;\n }\n\n section {\n padding: 24px;\n margin-bottom: 24px;\n width: 640px;\n }\n\n p { margin: 0 0 12px; font-size: 15px; }\n\n /* Readable grounds, each written in a color syntax a real browser keeps\n verbatim in getComputedStyle output. */\n .panel-srgb { background-color: color(srgb 0.1 0.11 0.12); }\n .panel-mix { background-color: color-mix(in srgb, white 6%, #0b0b0d); }\n .panel-p3 { background-color: color(display-p3 0.07 0.07 0.08); }\n .panel-lch { background-color: lch(18 4 60); }\n .panel-light { background-color: color(srgb 0.98 0.97 0.95); }\n\n /* A color space the detector does not model. Nested inside the light\n panel so a detector that walks PAST the unreadable layer would measure\n light text against the light section and invent a finding. */\n .panel-unreadable { background-color: color(rec2020 0.05 0.05 0.05); padding: 20px; }\n\n /* An image layer stacked ABOVE a loud gradient. The image is the visible\n surface and its pixels are unreadable; a detector that falls back to\n gradient stops here measures a layer the visitor never sees. */\n .panel-image-over-gradient { background-image: url(\"opaque-panel.png\"), linear-gradient(#ff0000, #0000ff); }\n\n /* Keywords a real browser resolves before getComputedStyle output but a\n partial cascade hands through verbatim: inherit takes the parent's\n ground, currentcolor paints with the element's own text color. Both are\n knowable surfaces; abstaining on them would hide real findings. */\n .panel-inherit { background-color: inherit; padding: 20px; }\n .panel-current { background-color: currentcolor; color: color(srgb 0.94 0.93 0.91); }\n .text-pale-2 { color: color(srgb 0.78 0.77 0.75); }\n\n .text-light { color: oklch(0.92 0.01 90); }\n .text-dark { color: color(srgb 0.1 0.11 0.12); }\n\n .text-muted-ground { color: oklch(0.32 0.01 90); }\n .text-dim-srgb { color: color(srgb 0.28 0.28 0.3); }\n .text-dim-p3 { color: color(display-p3 0.35 0.35 0.36); }\n .text-dim-lch { color: lch(35 3 60); }\n .text-pale-light { color: color(srgb 0.75 0.74 0.72); }\n","classText":"text-light\ntext-muted-ground\npanel-srgb\ntext-dim-srgb\npanel-p3\ntext-dim-p3\npanel-lch\ntext-dim-lch\npanel-light\ntext-pale-light\npanel-light\npanel-inherit\ntext-pale-2\npanel-current\ntext-pale-light\ntext-light\ntext-light\npanel-srgb\ntext-light\npanel-mix\ntext-light\npanel-p3\ntext-light\npanel-lch\ntext-light\npanel-light\ntext-dark\npanel-unreadable\ntext-light\npanel-image-over-gradient\ntext-muted-ground"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Edge-flush cards fixture (browser-only)</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #f2f2f4; }\n h2 { padding: 16px 24px 0; font-size: 16px; }\n .scroller { width: 600px; margin: 8px 24px 24px; overflow-x: auto; display: flex; scroll-snap-type: x mandatory; background: #e4e4e8; }\n .panel { flex: 0 0 auto; scroll-snap-align: start; }\n .card { background: #fff; border-radius: 10px; height: 90px; padding: 12px; box-sizing: border-box; margin-bottom: 12px; }\n .plain { height: 90px; padding: 12px; box-sizing: border-box; }\n\n /* FLAG: first panel is wider than the scroller's clip box, so its cards\n keep a 16px left gutter but end 4px past the right clip edge. */\n .flag-pager .panel-wide { width: 620px; }\n .flag-pager .panel-wide .card { margin-left: 16px; width: 588px; }\n .flag-pager .panel-ok { width: 600px; }\n .flag-pager .panel-ok .card { margin-left: 16px; width: 568px; }\n\n /* PASS: every panel keeps a symmetric inset. */\n .pass-even .panel { width: 600px; }\n .pass-even .card { margin-left: 16px; width: 568px; }\n\n /* PASS: deliberate peek carousel; the third card is cut mid-card, far\n past the clip edge, which reads as an affordance rather than a bug. */\n .pass-peek { gap: 12px; padding: 12px 0 12px 16px; }\n .pass-peek .card { flex: 0 0 auto; width: 260px; margin: 0; }\n\n /* PASS: flush children without card styling are plain content, not cards. */\n .pass-plain .panel { width: 620px; }\n .pass-plain .plain { margin-left: 16px; width: 588px; }\n </style>\n</head>\n<body>\n <h2>Flag: first panel wider than its clip box</h2>\n <div class=\"scroller flag-pager\">\n <div class=\"panel panel-wide\">\n <div class=\"card\">Card cut off at the right clip edge while keeping its left gutter.</div>\n <div class=\"card\">Second card in the oversized panel, also flush against the edge.</div>\n </div>\n <div class=\"panel panel-ok\">\n <div class=\"card\">Sibling panel card with the intended inset on both sides.</div>\n </div>\n </div>\n\n <h2>Pass: symmetric insets</h2>\n <div class=\"scroller pass-even\">\n <div class=\"panel\"><div class=\"card\">Card with an even gutter on both sides of the clip box.</div></div>\n <div class=\"panel\"><div class=\"card\">Second panel card, also properly inset.</div></div>\n </div>\n\n <h2>Pass: deliberate peek</h2>\n <div class=\"scroller pass-peek\">\n <div class=\"card\">First peek card fully visible.</div>\n <div class=\"card\">Second peek card fully visible.</div>\n <div class=\"card\">Third card cut mid-card as a scroll affordance.</div>\n <div class=\"card\">Fourth card entirely off-screen.</div>\n </div>\n\n <h2>Pass: plain content, not cards</h2>\n <div class=\"scroller pass-plain\">\n <div class=\"panel\"><div class=\"plain\">Unstyled text block flush against the edge is not a card.</div></div>\n <div class=\"panel\"><div class=\"plain\">Second plain block.</div></div>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #f2f2f4; }\n h2 { padding: 16px 24px 0; font-size: 16px; }\n .scroller { width: 600px; margin: 8px 24px 24px; overflow-x: auto; display: flex; scroll-snap-type: x mandatory; background: #e4e4e8; }\n .panel { flex: 0 0 auto; scroll-snap-align: start; }\n .card { background: #fff; border-radius: 10px; height: 90px; padding: 12px; box-sizing: border-box; margin-bottom: 12px; }\n .plain { height: 90px; padding: 12px; box-sizing: border-box; }\n\n /* FLAG: first panel is wider than the scroller's clip box, so its cards\n keep a 16px left gutter but end 4px past the right clip edge. */\n .flag-pager .panel-wide { width: 620px; }\n .flag-pager .panel-wide .card { margin-left: 16px; width: 588px; }\n .flag-pager .panel-ok { width: 600px; }\n .flag-pager .panel-ok .card { margin-left: 16px; width: 568px; }\n\n /* PASS: every panel keeps a symmetric inset. */\n .pass-even .panel { width: 600px; }\n .pass-even .card { margin-left: 16px; width: 568px; }\n\n /* PASS: deliberate peek carousel; the third card is cut mid-card, far\n past the clip edge, which reads as an affordance rather than a bug. */\n .pass-peek { gap: 12px; padding: 12px 0 12px 16px; }\n .pass-peek .card { flex: 0 0 auto; width: 260px; margin: 0; }\n\n /* PASS: flush children without card styling are plain content, not cards. */\n .pass-plain .panel { width: 620px; }\n .pass-plain .plain { margin-left: 16px; width: 588px; }\n ","classText":"scroller flag-pager\npanel panel-wide\ncard\ncard\npanel panel-ok\ncard\nscroller pass-even\npanel\ncard\npanel\ncard\nscroller pass-peek\ncard\ncard\ncard\ncard\nscroller pass-plain\npanel\nplain\npanel\nplain"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Em-dash entity overuse</title>\n</head>\n<body>\n <main>\n <h1>A page whose em-dashes hide inside HTML entities</h1>\n <p>\n The product is fast &mdash; it is also cheap &mdash; and it is honest\n &mdash; which matters &mdash; more than speed &mdash; or price\n &mdash; in the short term &mdash; and the long run &mdash; always.\n </p>\n </main>\n</body>\n</html>\n",{"styleText":"","classText":""}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Extreme negative tracking fixture</title>\n <style>\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n p, h2 { margin: 0 0 20px; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: tracking crushed past the point of legibility -->\n <section class=\"col flag\">\n <p style=\"letter-spacing: -0.06em\">Tracking crushed em paragraph copy that should still read as a sentence.</p>\n <p style=\"letter-spacing: -4px; font-size: 64px\">Tracking crushed pixels heading line</p>\n <h2 style=\"letter-spacing: -0.07em\">Tracking crushed heading variant text</h2>\n </section>\n\n <!-- PASS column: optical tightening that display type legitimately wants -->\n <section class=\"col pass\">\n <h1 style=\"letter-spacing: -0.02em; font-size: 72px\">Optical tighten display</h1>\n <p style=\"letter-spacing: normal\">Plain paragraph copy with default tracking that reads comfortably.</p>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n p, h2 { margin: 0 0 20px; }\n \nstyle=\"letter-spacing: -0.06em\"\nstyle=\"letter-spacing: -4px; font-size: 64px\"\nstyle=\"letter-spacing: -0.07em\"\nstyle=\"letter-spacing: -0.02em; font-size: 72px\"\nstyle=\"letter-spacing: normal\"","classText":"cols\ncol flag\ncol pass"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>first-viewport-column-overflow fixture</title>\n<style>\n * { margin: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; color: #111; background: #fff; }\n p { margin: 0 0 14px; line-height: 1.6; }\n\n /* ── FLAG: two-column opening section, right column runs far past the fold\n while the left column fits inside one viewport ── */\n .flag { display: grid; grid-template-columns: 1.4fr 1fr; gap: 40px; padding: 40px; }\n .flag .short { }\n .flag .short h1 { font-size: 40px; margin-bottom: 16px; }\n .flag .tall .filler { height: 1600px; background: #eee; border-radius: 8px; padding: 16px; }\n\n /* ── PASS: balanced two-column section, both fit ── */\n .pass-balanced { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; padding: 40px; }\n .pass-balanced .col { }\n\n /* ── PASS: single full-page hero (no sibling column) ── */\n .pass-hero { padding: 40px; }\n .pass-hero .art { height: 1400px; background: #f3f3f3; }\n</style>\n</head>\n<body>\n <section class=\"flag\">\n <div class=\"short\">\n <h1>Upload your film and add a narrator</h1>\n <p>Paste a link or drop a file. We transcribe it, translate, and one narrator reads over the top.</p>\n <p>The original stays audible underneath, just quieter.</p>\n </div>\n <aside class=\"tall\">\n <p>What is a lektor? A single calm voice reads every line of a foreign film.</p>\n <div class=\"filler\">A tall editorial column with a long photo and a lot of copy.</div>\n </aside>\n </section>\n\n <section class=\"pass-balanced\">\n <div class=\"col\">\n <p>Left column with a normal amount of copy that fits comfortably within the opening viewport.</p>\n <p>Two short paragraphs, nothing that runs long.</p>\n </div>\n <div class=\"col\">\n <p>Right column, also short. Both sides sit inside the first screen.</p>\n <p>No column stretches the fold.</p>\n </div>\n </section>\n\n <section class=\"pass-hero\">\n <div class=\"art\"></div>\n </section>\n</body>\n</html>\n",{"styleText":"\n * { margin: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; color: #111; background: #fff; }\n p { margin: 0 0 14px; line-height: 1.6; }\n\n /* ── FLAG: two-column opening section, right column runs far past the fold\n while the left column fits inside one viewport ── */\n .flag { display: grid; grid-template-columns: 1.4fr 1fr; gap: 40px; padding: 40px; }\n .flag .short { }\n .flag .short h1 { font-size: 40px; margin-bottom: 16px; }\n .flag .tall .filler { height: 1600px; background: #eee; border-radius: 8px; padding: 16px; }\n\n /* ── PASS: balanced two-column section, both fit ── */\n .pass-balanced { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; padding: 40px; }\n .pass-balanced .col { }\n\n /* ── PASS: single full-page hero (no sibling column) ── */\n .pass-hero { padding: 40px; }\n .pass-hero .art { height: 1400px; background: #f3f3f3; }\n","classText":"flag\nshort\ntall\nfiller\npass-balanced\ncol\ncol\npass-hero\nart"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Flush Against Border; Should Flag vs Should Pass</title>\n <style>\n /* Fixture for the flush-against-border rule.\n\n The rule is more general than its name suggests: it fires when a\n container has any visible boundary (border, outline, OR a\n non-transparent background) AND zero inline padding on the\n bounded side, AND text-bearing children land flush against that\n boundary.\n\n Distinct from cramped-padding (which fires when an element has\n its OWN direct text and the padding/font-size ratio is wrong).\n Here the container has NO direct text; only children; so\n cramped-padding gives it a pass. */\n\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 22px; font-weight: 700; margin: 0 0 16px; color: #0f172a; line-height: 1.2; }\n .col h3 { font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; max-width: 60ch; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD FLAG\n ──────────────────────────────────────────────────────────── */\n\n /* ── FLAG: the /live-mode-frameworks bug; border + zero side-padding ── */\n .flag-frameworks {\n padding: 28px 0 0;\n background: #fffbe6;\n border: 1px solid #d4a017;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .flag-frameworks-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #8b6914;\n }\n .flag-frameworks-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .flag-frameworks-list li {\n font-family: ui-monospace, monospace;\n font-size: 13px;\n padding: 6px 12px;\n background: #fef3c7;\n border: 1px solid #d4a017;\n border-radius: 4px;\n color: #78350f;\n }\n\n /* ── FLAG: child text flush against the four borders of a card ── */\n .flag-card-borders {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .flag-card-borders h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-card-borders p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: non-transparent background, zero padding ── */\n .flag-bg-only {\n padding: 0;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .flag-bg-only h4 { margin: 0; font-size: 14px; color: #78350f; }\n .flag-bg-only p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── FLAG: outline (no border), zero padding ── */\n .flag-outline-only {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n }\n .flag-outline-only h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-outline-only p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: border-left + zero left-padding, other sides fine ── */\n .flag-asym-leftflush {\n padding: 12px 16px 12px 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 4px;\n }\n .flag-asym-leftflush h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-asym-leftflush p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD PASS\n ──────────────────────────────────────────────────────────── */\n\n /* ── PASS: no boundary at all (no bg, no border, no outline) ── */\n .pass-no-boundary {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 0;\n }\n .pass-no-boundary-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-no-boundary-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-no-boundary-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: top-only border, no left/right border, padding-top fine ── */\n .pass-top-rule {\n padding: 24px 0 0;\n background: transparent;\n border: 0;\n border-top: 1px solid #cbd5e1;\n }\n .pass-top-rule-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-top-rule-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-top-rule-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: bordered card with comfortable inset on all sides ── */\n .pass-bordered-padded {\n padding: 18px 20px;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-bordered-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-bordered-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: background card with comfortable inset ── */\n .pass-bg-padded {\n padding: 14px 18px;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .pass-bg-padded h4 { margin: 0; font-size: 14px; color: #78350f; }\n .pass-bg-padded p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── PASS: outline with comfortable inset ── */\n .pass-outline-padded {\n padding: 14px 18px;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n border: 0;\n background: transparent;\n }\n .pass-outline-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-outline-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: image-only container, no text means no flush issue ── */\n .pass-image-only {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n width: 200px;\n overflow: hidden;\n }\n .pass-image-only .placeholder-img {\n width: 100%;\n height: 80px;\n background: linear-gradient(135deg, #cbd5e1, #94a3b8);\n }\n\n /* ── PASS: parent has zero padding, but child margins create the visible inset ── */\n .pass-margin-inset {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-margin-inset h4 { margin: 16px 18px 4px; font-size: 14px; color: #0f172a; }\n .pass-margin-inset p { margin: 0 18px 16px; font-size: 13px; color: #475569; }\n\n /* ── PASS: an inner shell, not the parent, owns the inset spacing ── */\n .pass-inner-shell {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-inner-shell .inner-shell {\n margin: 18px;\n }\n .pass-inner-shell h4 { margin: 0 0 4px; font-size: 14px; color: #0f172a; }\n .pass-inner-shell p { margin: 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: same background as parent, so the child draws no visible boundary ── */\n .pass-same-bg-parent {\n padding: 16px;\n background: #eef2f7;\n border-radius: 8px;\n }\n .pass-same-bg-child {\n padding: 0;\n background: #eef2f7;\n border: 0;\n border-radius: 8px;\n }\n .pass-same-bg-child h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-same-bg-child p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: full-bleed marquee/surface, not a text card missing padding ── */\n .pass-marquee-shell {\n padding: 0;\n background: #172033;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-marquee-track {\n display: flex;\n gap: 12px;\n width: max-content;\n }\n .pass-marquee-card {\n width: 220px;\n padding: 14px;\n background: #23304a;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-marquee-card p { margin: 0; font-size: 13px; }\n\n /* ── PASS: outer scene owns background; inner text surface owns padding ── */\n .pass-inner-text-surface {\n padding: 0;\n background: #0f172a;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-inner-text-surface-window {\n display: grid;\n gap: 6px;\n min-height: 92px;\n padding: 14px;\n background: #111827;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-inner-text-surface-window p { margin: 0; font-size: 13px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Border + zero side-padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">section: full border, padding 28px 0 0; label and pills touch the left/right border lines.</span>\n <section class=\"flag-frameworks\">\n <span class=\"flag-frameworks-label\">Supported dev servers</span>\n <ul class=\"flag-frameworks-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n <li>Astro</li>\n </ul>\n </section>\n </div>\n\n <h3>All sides flush against a border</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: 1px border on all sides, padding 0; heading and body touch every side.</span>\n <div class=\"flag-card-borders\">\n <h4>Card flush against the borders</h4>\n <p>Body copy sits flush against every visible border line.</p>\n </div>\n </div>\n\n <h3>Background color, zero padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">no border, but a non-transparent bg; text flush against the bg's edge counts too.</span>\n <div class=\"flag-bg-only\">\n <h4>Title flush against the bg edge</h4>\n <p>Body copy with zero padding around a colored fill.</p>\n </div>\n </div>\n\n <h3>Outline (not border), zero padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">outline draws outside the box; content at the box edge sits next to the outline.</span>\n <div class=\"flag-outline-only\">\n <h4>Title flush against the outline</h4>\n <p>The outline counts as a visible boundary too.</p>\n </div>\n </div>\n\n <h3>Left-side flush, other sides fine</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: padding 12px 16px 12px 0; text touches the left border, other sides are inset.</span>\n <div class=\"flag-asym-leftflush\">\n <h4>Title flush only on the left</h4>\n <p>Padding-left zero with a full border.</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>No boundary at all</h3>\n <div class=\"case\">\n <span class=\"case-label\">no bg, no border, no outline; zero padding is fine.</span>\n <section class=\"pass-no-boundary\">\n <span class=\"pass-no-boundary-label\">Supported dev servers</span>\n <ul class=\"pass-no-boundary-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n </ul>\n </section>\n </div>\n\n <h3>Top-only border, no side borders</h3>\n <div class=\"case\">\n <span class=\"case-label\">border-top only, padding-top 24px; no left/right border to be flush against.</span>\n <section class=\"pass-top-rule\">\n <span class=\"pass-top-rule-label\">Supported dev servers</span>\n <ul class=\"pass-top-rule-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n </ul>\n </section>\n </div>\n\n <h3>Bordered card with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: 1px border + padding 18px 20px; comfortable inset on every side.</span>\n <div class=\"pass-bordered-padded\">\n <h4>Card with proper inset</h4>\n <p>Body copy sits comfortably away from the borders.</p>\n </div>\n </div>\n\n <h3>Background card with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">non-transparent bg + padding 14px 18px; content has room to breathe.</span>\n <div class=\"pass-bg-padded\">\n <h4>Title with inset on bg</h4>\n <p>Body copy is comfortably inset from the bg edge.</p>\n </div>\n </div>\n\n <h3>Outline with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">outline + padding 14px 18px; content is comfortably inside.</span>\n <div class=\"pass-outline-padded\">\n <h4>Title with inset and outline</h4>\n <p>Outline boundary with proper inset works.</p>\n </div>\n </div>\n\n <h3>Image-only container, no text</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: padding 0, border all sides; no text means nothing can be flush.</span>\n <div class=\"pass-image-only\">\n <div class=\"placeholder-img\"></div>\n </div>\n </div>\n\n <h3>Child margins provide the inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: parent padding 0, but text children have margins that create visible inset.</span>\n <div class=\"pass-margin-inset\">\n <h4>Title inset by child margin</h4>\n <p>Body copy is not visually flush even though the parent has zero padding.</p>\n </div>\n </div>\n\n <h3>Inner shell provides the inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: parent padding 0, but an inner shell is offset from every edge.</span>\n <div class=\"pass-inner-shell\">\n <div class=\"inner-shell\">\n <h4>Title inset by inner shell</h4>\n <p>The direct child creates the breathing room.</p>\n </div>\n </div>\n </div>\n\n <h3>Same background as parent</h3>\n <div class=\"case\">\n <span class=\"case-label\">child padding 0, but it has the same background as the parent, so no child boundary is visible.</span>\n <div class=\"pass-same-bg-parent\">\n <div class=\"pass-same-bg-child\">\n <h4>Same-surface visual shell</h4>\n <p>The text belongs to the parent surface, not to a distinct zero-padding card.</p>\n </div>\n </div>\n </div>\n\n <h3>Full-bleed marquee shell</h3>\n <div class=\"case\">\n <span class=\"case-label\">marquee shell clips animated cards intentionally; the cards, not the shell, own text padding.</span>\n <section class=\"pass-marquee-shell\">\n <div class=\"pass-marquee-track\">\n <article class=\"pass-marquee-card\"><p>Quote card with its own comfortable padding.</p></article>\n <article class=\"pass-marquee-card\"><p>Another moving card, also padded inside.</p></article>\n </div>\n </section>\n </div>\n\n <h3>Inner text surface</h3>\n <div class=\"case\">\n <span class=\"case-label\">outer shell has a scene background; the inner window owns the actual text padding.</span>\n <div class=\"pass-inner-text-surface\">\n <div class=\"pass-inner-text-surface-window\">\n <p>Build step running</p>\n <p>Detector output belongs to the mock window.</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Fixture for the flush-against-border rule.\n\n The rule is more general than its name suggests: it fires when a\n container has any visible boundary (border, outline, OR a\n non-transparent background) AND zero inline padding on the\n bounded side, AND text-bearing children land flush against that\n boundary.\n\n Distinct from cramped-padding (which fires when an element has\n its OWN direct text and the padding/font-size ratio is wrong).\n Here the container has NO direct text; only children; so\n cramped-padding gives it a pass. */\n\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 22px; font-weight: 700; margin: 0 0 16px; color: #0f172a; line-height: 1.2; }\n .col h3 { font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; max-width: 60ch; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD FLAG\n ──────────────────────────────────────────────────────────── */\n\n /* ── FLAG: the /live-mode-frameworks bug; border + zero side-padding ── */\n .flag-frameworks {\n padding: 28px 0 0;\n background: #fffbe6;\n border: 1px solid #d4a017;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .flag-frameworks-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #8b6914;\n }\n .flag-frameworks-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .flag-frameworks-list li {\n font-family: ui-monospace, monospace;\n font-size: 13px;\n padding: 6px 12px;\n background: #fef3c7;\n border: 1px solid #d4a017;\n border-radius: 4px;\n color: #78350f;\n }\n\n /* ── FLAG: child text flush against the four borders of a card ── */\n .flag-card-borders {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .flag-card-borders h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-card-borders p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: non-transparent background, zero padding ── */\n .flag-bg-only {\n padding: 0;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .flag-bg-only h4 { margin: 0; font-size: 14px; color: #78350f; }\n .flag-bg-only p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── FLAG: outline (no border), zero padding ── */\n .flag-outline-only {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n }\n .flag-outline-only h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-outline-only p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: border-left + zero left-padding, other sides fine ── */\n .flag-asym-leftflush {\n padding: 12px 16px 12px 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 4px;\n }\n .flag-asym-leftflush h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-asym-leftflush p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD PASS\n ──────────────────────────────────────────────────────────── */\n\n /* ── PASS: no boundary at all (no bg, no border, no outline) ── */\n .pass-no-boundary {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 0;\n }\n .pass-no-boundary-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-no-boundary-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-no-boundary-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: top-only border, no left/right border, padding-top fine ── */\n .pass-top-rule {\n padding: 24px 0 0;\n background: transparent;\n border: 0;\n border-top: 1px solid #cbd5e1;\n }\n .pass-top-rule-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-top-rule-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-top-rule-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: bordered card with comfortable inset on all sides ── */\n .pass-bordered-padded {\n padding: 18px 20px;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-bordered-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-bordered-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: background card with comfortable inset ── */\n .pass-bg-padded {\n padding: 14px 18px;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .pass-bg-padded h4 { margin: 0; font-size: 14px; color: #78350f; }\n .pass-bg-padded p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── PASS: outline with comfortable inset ── */\n .pass-outline-padded {\n padding: 14px 18px;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n border: 0;\n background: transparent;\n }\n .pass-outline-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-outline-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: image-only container, no text means no flush issue ── */\n .pass-image-only {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n width: 200px;\n overflow: hidden;\n }\n .pass-image-only .placeholder-img {\n width: 100%;\n height: 80px;\n background: linear-gradient(135deg, #cbd5e1, #94a3b8);\n }\n\n /* ── PASS: parent has zero padding, but child margins create the visible inset ── */\n .pass-margin-inset {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-margin-inset h4 { margin: 16px 18px 4px; font-size: 14px; color: #0f172a; }\n .pass-margin-inset p { margin: 0 18px 16px; font-size: 13px; color: #475569; }\n\n /* ── PASS: an inner shell, not the parent, owns the inset spacing ── */\n .pass-inner-shell {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-inner-shell .inner-shell {\n margin: 18px;\n }\n .pass-inner-shell h4 { margin: 0 0 4px; font-size: 14px; color: #0f172a; }\n .pass-inner-shell p { margin: 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: same background as parent, so the child draws no visible boundary ── */\n .pass-same-bg-parent {\n padding: 16px;\n background: #eef2f7;\n border-radius: 8px;\n }\n .pass-same-bg-child {\n padding: 0;\n background: #eef2f7;\n border: 0;\n border-radius: 8px;\n }\n .pass-same-bg-child h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-same-bg-child p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: full-bleed marquee/surface, not a text card missing padding ── */\n .pass-marquee-shell {\n padding: 0;\n background: #172033;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-marquee-track {\n display: flex;\n gap: 12px;\n width: max-content;\n }\n .pass-marquee-card {\n width: 220px;\n padding: 14px;\n background: #23304a;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-marquee-card p { margin: 0; font-size: 13px; }\n\n /* ── PASS: outer scene owns background; inner text surface owns padding ── */\n .pass-inner-text-surface {\n padding: 0;\n background: #0f172a;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-inner-text-surface-window {\n display: grid;\n gap: 6px;\n min-height: 92px;\n padding: 14px;\n background: #111827;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-inner-text-surface-window p { margin: 0; font-size: 13px; }\n ","classText":"grid\ncol\ncase\ncase-label\nflag-frameworks\nflag-frameworks-label\nflag-frameworks-list\ncase\ncase-label\nflag-card-borders\ncase\ncase-label\nflag-bg-only\ncase\ncase-label\nflag-outline-only\ncase\ncase-label\nflag-asym-leftflush\ncol\ncase\ncase-label\npass-no-boundary\npass-no-boundary-label\npass-no-boundary-list\ncase\ncase-label\npass-top-rule\npass-top-rule-label\npass-top-rule-list\ncase\ncase-label\npass-bordered-padded\ncase\ncase-label\npass-bg-padded\ncase\ncase-label\npass-outline-padded\ncase\ncase-label\npass-image-only\nplaceholder-img\ncase\ncase-label\npass-margin-inset\ncase\ncase-label\npass-inner-shell\ninner-shell\ncase\ncase-label\npass-same-bg-parent\npass-same-bg-child\ncase\ncase-label\npass-marquee-shell\npass-marquee-track\npass-marquee-card\npass-marquee-card\ncase\ncase-label\npass-inner-text-surface\npass-inner-text-surface-window"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Image hover-transform fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n img { display: block; width: 160px; height: 120px; margin: 0 0 24px; background: #ddd; }\n .flag-css-hover img { transition: transform 0.3s; }\n .flag-css-hover img:hover { transform: scale(1.05); }\n .card:hover { transform: translateY(-4px); }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: image hover transforms -->\n <section class=\"col flag\">\n <div class=\"flag-css-hover\">\n <img src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23c9b89a'/%3E%3C/svg%3E\" alt=\"CSS hover-zoom image\" />\n </div>\n <img class=\"flag-tw-hover hover:scale-105 transition-transform\" src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23a89c86'/%3E%3C/svg%3E\" alt=\"Tailwind hover-zoom image\" />\n </section>\n\n <!-- PASS column: static images, and hover transforms on non-image elements -->\n <section class=\"col pass\">\n <img class=\"pass-static\" src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23ddd6c8'/%3E%3C/svg%3E\" alt=\"Static image with no hover transform\" />\n <div class=\"card pass-non-img-hover\">A card that lifts on hover, which is fine because it is not an image.</div>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n img { display: block; width: 160px; height: 120px; margin: 0 0 24px; background: #ddd; }\n .flag-css-hover img { transition: transform 0.3s; }\n .flag-css-hover img:hover { transform: scale(1.05); }\n .card:hover { transform: translateY(-4px); }\n ","classText":"cols\ncol flag\nflag-css-hover\nflag-tw-hover hover:scale-105 transition-transform\ncol pass\npass-static\ncard pass-non-img-hover"}],"result":[{"id":"image-hover-transform","snippet":"img:hover { transform } rule","selector":".flag-css-hover img:hover"},{"id":"image-hover-transform","snippet":"Tailwind hover transform on <img>"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Dark Glow Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n /* Two-column fixture: left = should flag, right = should pass.\n The dark-glow rule fires when a colored box-shadow appears on a\n child whose ancestor has a dark background. Each column wraps\n its dark-context cases in its own .dark-context section so the\n parent-bg lookup behaves predictably regardless of layout. */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .dark-context { background: #111827; padding: 16px; border-radius: 12px; }\n .dark-context + .dark-context { margin-top: 12px; }\n /* Card variants: explicit dark vs light pairings, both WCAG AA compliant */\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .card h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .card p { font-size: 12px; margin: 4px 0 0; }\n .card-dark { background: #1f2937; }\n .card-dark h4 { color: #f9fafb; }\n .card-dark p { color: #cbd5e1; }\n .card-light { background: white; }\n .card-light h4 { color: #0f172a; }\n .card-light p { color: #475569; }\n\n /* ── FLAG: dark background + colored glow ── */\n .glow-blue { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }\n .glow-purple { box-shadow: 0 0 25px rgba(139, 92, 246, 0.35); }\n .glow-cyan { box-shadow: 0 0 15px rgba(6, 182, 212, 0.5); }\n .glow-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 0 30px rgba(168, 85, 247, 0.3); }\n\n /* ── FLAG: modern color formats, var() refs, text-shadow ── */\n :root { --fixture-glow: oklch(0.78 0.14 155); }\n .glow-oklch { box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5); }\n .glow-hex { box-shadow: 0 0 16px #3b82f6; }\n .glow-hsl { box-shadow: 0 0 22px hsl(280, 80%, 60%); }\n .glow-var { box-shadow: 0 0 10px var(--fixture-glow); }\n .glow-text h4 { text-shadow: 0 0 12px #22d3ee; }\n /* Zero-offset chromatic halo is slop on light backgrounds too */\n .glow-light-oklch { box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45); }\n /* Unreadable surface: url() image ancestor. The zero-offset halo tell\n holds on ANY background, so an unknown surface must not suppress it.\n Both element loops must agree here (the browser adapter once returned\n [] for the whole element on an unresolved surface). */\n .photo-context { background-image: url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-halo { box-shadow: 0 0 26px rgba(217, 70, 239, 0.5); }\n /* Opaque gradient atop a url() layer: every stop is opaque, so the\n gradient provably covers the image — the surface IS the dark\n gradient, and the dark-background glow tell may score against it\n (browser hunt; the static loop has no gradient hunt). */\n .photo-opaque-grad { background: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-opaque { box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5); }\n\n /* ── PASS: same dark/light backgrounds, but neutral or no glow ── */\n .light-colored-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.15); }\n .dark-normal-shadow { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); }\n .dark-focus-ring { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); }\n .dark-subtle-shadow { box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2); }\n .dark-elevated-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); }\n .dark-elevated-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); }\n .dark-elevated-lg { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); }\n .dark-elevated-inset { box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); }\n .dark-elevated-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); }\n .elevated-sm-light { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }\n .elevated-md-light { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }\n .elevated-warm-light { box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.15); }\n /* Medium-gray card: lighter than dark mode, used to demonstrate \"not dark enough\" for glow detection. White text for AA contrast. */\n .card-medium { background: #6b7280; }\n .card-medium h4 { color: #ffffff; }\n .card-medium p { color: #ffffff; }\n .medium-gray-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3); }\n /* Offset chromatic drop shadow on a light background: a real drop\n shadow, not a halo — must NOT flag under the zero-offset rule.\n (Element path; the page-level text scan caps at one finding.) */\n .light-offset-colored { box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25); }\n /* Achromatic zero-offset shadow: soft ambient elevation, stays legal */\n .light-soft-neutral { box-shadow: 0 0 24px rgba(0, 0, 0, 0.15); }\n /* Offset chromatic shadow under the same unreadable url() surface:\n the dark-background glow tell needs a surface we can read — abstain. */\n .photo-offset-colored { box-shadow: 0 6px 20px rgba(16, 185, 129, 0.35); }\n /* Translucent gradient atop a url() layer: the image shows through the\n 20% wash, so the real surface is a blend with unknowable pixels (a\n white photo composites to ~#cccccc, not black). Averaging the wash\n stops as if they were the surface scored this \"dark\" — abstain. */\n .photo-translucent-grad { background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .photo-translucent-offset { box-shadow: 0 6px 22px rgba(244, 63, 94, 0.45); }\n /* Offset neutral text-shadow on dark card: legibility aid, not a glow */\n .dark-text-offset h4 { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag (dark page + colored glows)\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>CSS colored glows on dark background</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark glow-blue\">\n <h4>Blue glow</h4>\n <p>box-shadow: 0 0 20px rgba(59, 130, 246, 0.4)</p>\n </div>\n <div class=\"card card-dark glow-purple\">\n <h4>Purple glow</h4>\n <p>box-shadow: 0 0 25px rgba(139, 92, 246, 0.35)</p>\n </div>\n <div class=\"card card-dark glow-cyan\">\n <h4>Cyan glow</h4>\n <p>box-shadow: 0 0 15px rgba(6, 182, 212, 0.5)</p>\n </div>\n <div class=\"card card-dark glow-multi\">\n <h4>Multi-shadow with colored glow</h4>\n <p>Normal shadow + purple glow combined.</p>\n </div>\n </div>\n\n <h3>Inline-style glow (regex path)</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark\" style=\"box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);\">\n <h4>Inline pink glow</h4>\n <p>Inline style for regex detection path.</p>\n </div>\n </div>\n\n <h3>Modern color formats, var() refs, text-shadow</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark glow-oklch\">\n <h4>OKLCH glow</h4>\n <p>box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5)</p>\n </div>\n <div class=\"card card-dark glow-hex\">\n <h4>Hex glow</h4>\n <p>box-shadow: 0 0 16px #3b82f6</p>\n </div>\n <div class=\"card card-dark glow-hsl\">\n <h4>HSL glow</h4>\n <p>box-shadow: 0 0 22px hsl(280, 80%, 60%)</p>\n </div>\n <div class=\"card card-dark glow-var\">\n <h4>Glow via var()</h4>\n <p>box-shadow: 0 0 10px var(--fixture-glow)</p>\n </div>\n <div class=\"card card-dark glow-text\">\n <h4>Glowing text</h4>\n <p>text-shadow: 0 0 12px #22d3ee on the heading.</p>\n </div>\n </div>\n\n <h3>Zero-offset halo on LIGHT background</h3>\n <div class=\"card card-light glow-light-oklch\">\n <h4>Chromatic halo on light page</h4>\n <p>box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45)</p>\n </div>\n\n <h3>Zero-offset halo on unreadable (image) surface</h3>\n <div class=\"photo-context\">\n <div class=\"card card-light glow-photo-halo\">\n <h4>Halo over photo background</h4>\n <p>box-shadow: 0 0 26px rgba(217, 70, 239, 0.5)</p>\n </div>\n </div>\n\n <h3>Colored glow under an opaque gradient over an image (browser)</h3>\n <div class=\"photo-opaque-grad\">\n <div class=\"card card-dark glow-photo-opaque\">\n <h4>Opaque dark gradient covers the photo</h4>\n <p>box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5)</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Light page + colored shadow</h3>\n <div class=\"card card-light light-colored-shadow\">\n <h4>Colored shadow on light background</h4>\n <p>Not dark mode, colored shadow is fine.</p>\n </div>\n\n <h3>Dark cards with neutral shadows</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark dark-normal-shadow\">\n <h4>Normal gray shadow on dark card</h4>\n <p>Gray/black shadow is not a glow.</p>\n </div>\n <div class=\"card card-dark dark-focus-ring\">\n <h4>Focus ring (spread, no blur)</h4>\n <p>Functional ring, not decorative glow.</p>\n </div>\n <div class=\"card card-dark dark-subtle-shadow\">\n <h4>Tiny blur (&lt;5px)</h4>\n <p>Too subtle to be \"glowing\".</p>\n </div>\n <div class=\"card card-dark dark-elevated-sm\">\n <h4>Small dark elevation</h4>\n <p>Subtle shadow on dark card.</p>\n </div>\n <div class=\"card card-dark dark-elevated-md\">\n <h4>Medium dark elevation</h4>\n <p>Standard dark card shadow.</p>\n </div>\n <div class=\"card card-dark dark-elevated-lg\">\n <h4>Large dark elevation</h4>\n <p>Prominent shadow on dark card.</p>\n </div>\n <div class=\"card card-dark dark-elevated-inset\">\n <h4>Inset shadow</h4>\n <p>Inner shadow, not a glow.</p>\n </div>\n <div class=\"card card-dark dark-elevated-multi\">\n <h4>Multi-shadow dark card</h4>\n <p>Layered gray shadows, no color glow.</p>\n </div>\n </div>\n\n <h3>Medium-gray background (not dark enough)</h3>\n <div class=\"card card-medium medium-gray-shadow\">\n <h4>Not dark enough background</h4>\n <p>Medium gray doesn't qualify as \"dark mode\".</p>\n </div>\n\n <h3>Colored drop shadow / neutral halo on light background</h3>\n <div class=\"card card-light light-offset-colored\">\n <h4>Offset colored drop shadow</h4>\n <p>Real drop shadow with a color tint — not a halo.</p>\n </div>\n <div class=\"card card-light light-soft-neutral\">\n <h4>Soft neutral halo</h4>\n <p>Achromatic zero-offset shadow is ambient elevation.</p>\n </div>\n\n <h3>Offset chromatic shadow on unreadable (image) surface</h3>\n <div class=\"photo-context\">\n <div class=\"card card-light photo-offset-colored\">\n <h4>Offset colored shadow over photo background</h4>\n <p>Surface unknown, offset shadow — abstain, not flag.</p>\n </div>\n </div>\n\n <h3>Offset chromatic shadow under a translucent gradient over an image</h3>\n <div class=\"photo-translucent-grad\">\n <div class=\"card card-light photo-translucent-offset\">\n <h4>The wash blends with unknowable image pixels</h4>\n <p>Surface unknown — abstain, never score the wash stops as dark.</p>\n </div>\n </div>\n\n <h3>Neutral text-shadow on dark card</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark dark-text-offset\">\n <h4>Offset neutral text-shadow</h4>\n <p>Legibility aid, not a glow.</p>\n </div>\n </div>\n\n <h3>Light elevated cards</h3>\n <div class=\"card card-light elevated-sm-light\">\n <h4>Small elevation (Tailwind shadow-sm)</h4>\n <p>Standard subtle card shadow.</p>\n </div>\n <div class=\"card card-light elevated-md-light\">\n <h4>Medium elevation (Tailwind shadow-md)</h4>\n <p>Standard card shadow.</p>\n </div>\n <div class=\"card card-light elevated-warm-light\">\n <h4>Warm deep shadow</h4>\n <p>Large spread neutral shadow.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left = should flag, right = should pass.\n The dark-glow rule fires when a colored box-shadow appears on a\n child whose ancestor has a dark background. Each column wraps\n its dark-context cases in its own .dark-context section so the\n parent-bg lookup behaves predictably regardless of layout. */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .dark-context { background: #111827; padding: 16px; border-radius: 12px; }\n .dark-context + .dark-context { margin-top: 12px; }\n /* Card variants: explicit dark vs light pairings, both WCAG AA compliant */\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .card h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .card p { font-size: 12px; margin: 4px 0 0; }\n .card-dark { background: #1f2937; }\n .card-dark h4 { color: #f9fafb; }\n .card-dark p { color: #cbd5e1; }\n .card-light { background: white; }\n .card-light h4 { color: #0f172a; }\n .card-light p { color: #475569; }\n\n /* ── FLAG: dark background + colored glow ── */\n .glow-blue { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }\n .glow-purple { box-shadow: 0 0 25px rgba(139, 92, 246, 0.35); }\n .glow-cyan { box-shadow: 0 0 15px rgba(6, 182, 212, 0.5); }\n .glow-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 0 30px rgba(168, 85, 247, 0.3); }\n\n /* ── FLAG: modern color formats, var() refs, text-shadow ── */\n :root { --fixture-glow: oklch(0.78 0.14 155); }\n .glow-oklch { box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5); }\n .glow-hex { box-shadow: 0 0 16px #3b82f6; }\n .glow-hsl { box-shadow: 0 0 22px hsl(280, 80%, 60%); }\n .glow-var { box-shadow: 0 0 10px var(--fixture-glow); }\n .glow-text h4 { text-shadow: 0 0 12px #22d3ee; }\n /* Zero-offset chromatic halo is slop on light backgrounds too */\n .glow-light-oklch { box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45); }\n /* Unreadable surface: url() image ancestor. The zero-offset halo tell\n holds on ANY background, so an unknown surface must not suppress it.\n Both element loops must agree here (the browser adapter once returned\n [] for the whole element on an unresolved surface). */\n .photo-context { background-image: url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-halo { box-shadow: 0 0 26px rgba(217, 70, 239, 0.5); }\n /* Opaque gradient atop a url() layer: every stop is opaque, so the\n gradient provably covers the image — the surface IS the dark\n gradient, and the dark-background glow tell may score against it\n (browser hunt; the static loop has no gradient hunt). */\n .photo-opaque-grad { background: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-opaque { box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5); }\n\n /* ── PASS: same dark/light backgrounds, but neutral or no glow ── */\n .light-colored-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.15); }\n .dark-normal-shadow { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); }\n .dark-focus-ring { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); }\n .dark-subtle-shadow { box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2); }\n .dark-elevated-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); }\n .dark-elevated-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); }\n .dark-elevated-lg { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); }\n .dark-elevated-inset { box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); }\n .dark-elevated-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); }\n .elevated-sm-light { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }\n .elevated-md-light { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }\n .elevated-warm-light { box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.15); }\n /* Medium-gray card: lighter than dark mode, used to demonstrate \"not dark enough\" for glow detection. White text for AA contrast. */\n .card-medium { background: #6b7280; }\n .card-medium h4 { color: #ffffff; }\n .card-medium p { color: #ffffff; }\n .medium-gray-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3); }\n /* Offset chromatic drop shadow on a light background: a real drop\n shadow, not a halo — must NOT flag under the zero-offset rule.\n (Element path; the page-level text scan caps at one finding.) */\n .light-offset-colored { box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25); }\n /* Achromatic zero-offset shadow: soft ambient elevation, stays legal */\n .light-soft-neutral { box-shadow: 0 0 24px rgba(0, 0, 0, 0.15); }\n /* Offset chromatic shadow under the same unreadable url() surface:\n the dark-background glow tell needs a surface we can read — abstain. */\n .photo-offset-colored { box-shadow: 0 6px 20px rgba(16, 185, 129, 0.35); }\n /* Translucent gradient atop a url() layer: the image shows through the\n 20% wash, so the real surface is a blend with unknowable pixels (a\n white photo composites to ~#cccccc, not black). Averaging the wash\n stops as if they were the surface scored this \"dark\" — abstain. */\n .photo-translucent-grad { background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .photo-translucent-offset { box-shadow: 0 6px 22px rgba(244, 63, 94, 0.45); }\n /* Offset neutral text-shadow on dark card: legibility aid, not a glow */\n .dark-text-offset h4 { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); }\n \nstyle=\"box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);\"","classText":"grid\ncol\ndark-context\ncard card-dark glow-blue\ncard card-dark glow-purple\ncard card-dark glow-cyan\ncard card-dark glow-multi\ndark-context\ncard card-dark\ndark-context\ncard card-dark glow-oklch\ncard card-dark glow-hex\ncard card-dark glow-hsl\ncard card-dark glow-var\ncard card-dark glow-text\ncard card-light glow-light-oklch\nphoto-context\ncard card-light glow-photo-halo\nphoto-opaque-grad\ncard card-dark glow-photo-opaque\ncol\ncard card-light light-colored-shadow\ndark-context\ncard card-dark dark-normal-shadow\ncard card-dark dark-focus-ring\ncard card-dark dark-subtle-shadow\ncard card-dark dark-elevated-sm\ncard card-dark dark-elevated-md\ncard card-dark dark-elevated-lg\ncard card-dark dark-elevated-inset\ncard card-dark dark-elevated-multi\ncard card-medium medium-gray-shadow\ncard card-light light-offset-colored\ncard card-light light-soft-neutral\nphoto-context\ncard card-light photo-offset-colored\nphoto-translucent-grad\ncard card-light photo-translucent-offset\ndark-context\ncard card-dark dark-text-offset\ncard card-light elevated-sm-light\ncard card-light elevated-md-light\ncard card-light elevated-warm-light"}],"result":[{"id":"dark-glow","snippet":"Zero-offset box-shadow glow (#3b82f6)","selector":"/* ── FLAG: dark background + colored glow ── */ .glow-blue"}]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Recurring generated-UI tell fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .card { padding: 24px; margin: 0 0 24px; border-radius: 12px; }\n .stripes { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n .grid { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: recurring generated-UI idioms -->\n <section class=\"col flag\">\n <div class=\"card flag-thin-border-wide-shadow\" style=\"border: 1px solid #e5e7eb; box-shadow: 0 0 24px rgba(0,0,0,0.18)\">\n Thin hairline border paired with a wide diffuse shadow.\n </div>\n <div class=\"stripes flag-repeating-stripes\" style=\"background: repeating-linear-gradient(45deg, #eee, #eee 10px, #fafafa 10px, #fafafa 20px)\"></div>\n <div class=\"grid flag-codex-grid\" style=\"background: linear-gradient(90deg, oklch(96% 0.012 82 / 0.055) 1px, transparent 1px), linear-gradient(180deg, oklch(96% 0.012 82 / 0.05) 1px, transparent 1px); background-size: 78px 78px, 78px 78px;\"></div>\n <p class=\"flag-theater\">We retired the growth theater and shipped something that actually works.</p>\n </section>\n\n <!-- PASS column: the legitimate, non-tell versions -->\n <section class=\"col pass\">\n <div class=\"card pass-tight-shadow\" style=\"border: 1px solid #e5e7eb; box-shadow: 0 1px 4px rgba(0,0,0,0.08)\">\n Hairline border with a tight, purposeful shadow.\n </div>\n <div class=\"card pass-soft-border-wide-shadow\" style=\"border: 1px solid rgba(148, 163, 184, 0.16); box-shadow: 0 18px 48px -16px rgba(15, 23, 42, 0.18)\">\n A quiet low-contrast hairline with a broad but restrained ambient shadow.\n </div>\n <div class=\"card pass-oklch-soft-shadow\" style=\"border: 1px solid oklch(78% 0 0 / 0.16); box-shadow: 0 16px 36px -12px oklch(2% 0.004 95 / 0.18)\">\n OKLCH shadow hue values should not be mistaken for shadow blur.\n </div>\n <div class=\"stripes pass-plain-gradient\" style=\"background: linear-gradient(180deg, #f3f4f6, #ffffff)\"></div>\n <div class=\"grid pass-single-axis-rule\" style=\"background: linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 100% 32px\"></div>\n <div class=\"grid pass-two-color-blend\" style=\"background: linear-gradient(90deg, #f3f4f6, #e5e7eb), linear-gradient(180deg, #eef2ff, #ffffff)\"></div>\n <div class=\"grid pass-crosshair-no-tiling\" style=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px), linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px)\"></div>\n <div class=\"grid pass-bg-plus-mask-hairline\" style=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 32px 32px; mask-image: linear-gradient(180deg, #000 1px, transparent 1px)\"></div>\n <p class=\"pass-no-theater\">We measured real outcomes for the people who use this every day.</p>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .card { padding: 24px; margin: 0 0 24px; border-radius: 12px; }\n .stripes { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n .grid { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n \nstyle=\"border: 1px solid #e5e7eb; box-shadow: 0 0 24px rgba(0,0,0,0.18)\"\nstyle=\"background: repeating-linear-gradient(45deg, #eee, #eee 10px, #fafafa 10px, #fafafa 20px)\"\nstyle=\"background: linear-gradient(90deg, oklch(96% 0.012 82 / 0.055) 1px, transparent 1px), linear-gradient(180deg, oklch(96% 0.012 82 / 0.05) 1px, transparent 1px); background-size: 78px 78px, 78px 78px;\"\nstyle=\"border: 1px solid #e5e7eb; box-shadow: 0 1px 4px rgba(0,0,0,0.08)\"\nstyle=\"border: 1px solid rgba(148, 163, 184, 0.16); box-shadow: 0 18px 48px -16px rgba(15, 23, 42, 0.18)\"\nstyle=\"border: 1px solid oklch(78% 0 0 / 0.16); box-shadow: 0 16px 36px -12px oklch(2% 0.004 95 / 0.18)\"\nstyle=\"background: linear-gradient(180deg, #f3f4f6, #ffffff)\"\nstyle=\"background: linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 100% 32px\"\nstyle=\"background: linear-gradient(90deg, #f3f4f6, #e5e7eb), linear-gradient(180deg, #eef2ff, #ffffff)\"\nstyle=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px), linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px)\"\nstyle=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 32px 32px; mask-image: linear-gradient(180deg, #000 1px, transparent 1px)\"","classText":"cols\ncol flag\ncard flag-thin-border-wide-shadow\nstripes flag-repeating-stripes\ngrid flag-codex-grid\nflag-theater\ncol pass\ncard pass-tight-shadow\ncard pass-soft-border-wide-shadow\ncard pass-oklch-soft-shadow\nstripes pass-plain-gradient\ngrid pass-single-axis-rule\ngrid pass-two-color-blend\ngrid pass-crosshair-no-tiling\ngrid pass-bg-plus-mask-hairline\npass-no-theater"}],"result":[{"id":"repeating-stripes-gradient","snippet":"repeating-gradient decorative stripes","selector":".grid"},{"id":"codex-grid-background","snippet":"two-axis grid-line gradient background","selector":".grid"},{"id":"theater-slop-phrase","snippet":"\"growth theater\""}]}
{"args":["<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>Heading rhythm fixture</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: Georgia, serif; font-size: 16px; line-height: 1.5; width: 720px; }\n h2 { font-size: 28px; line-height: 1.2; }\n h3 { font-size: 20px; line-height: 1.2; }\n p { max-width: 60ch; }\n\n /* FLAG: zero space above the heading, clear space below it */\n .flag-crowded { margin: 0 0 40px; }\n .flag-crowded + p { margin: 0 0 24px; }\n\n /* PASS: generous space above, tight below */\n .pass-generous { margin: 64px 0 16px; }\n\n /* PASS: near-equal spacing is ambiguous, not inverted */\n .pass-near-equal { margin: 20px 0 24px; }\n\n /* PASS: eyebrow label bound to the heading; real space sits above the cluster */\n .pass-eyebrow-label { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; margin: 72px 0 8px; }\n .pass-eyebrow-heading { margin: 0 0 24px; }\n\n /* PASS: small card, its boundary provides the separation */\n .pass-card { box-shadow: 0 1px 4px rgba(0,0,0,0.2); background: #f4f4f4; padding: 16px; margin: 48px 0; width: 320px; }\n .pass-card h3 { margin: 0 0 20px; }\n\n /* PASS: heading first inside a banded section; the band edge separates */\n .pass-band { background: #ececec; padding: 8px 0 32px; margin-top: 8px; }\n .pass-band h2 { margin: 0 0 24px; }\n</style>\n</head>\n<body>\n\n <h2 class=\"pass-first\">Pass First Heading</h2>\n <p>Opening paragraph after the first heading on the page. The first heading has\n nothing above it, so it is exempt from the rhythm comparison entirely.</p>\n\n <p style=\"margin-top: 24px;\">A closing paragraph of the previous block of content,\n sitting directly above the crowded heading with no gap at all between them.</p>\n <h2 class=\"flag-crowded\">Flag Crowded One</h2>\n <p>The content this heading introduces sits forty pixels away while the previous\n block touches the heading, so the heading reads as a caption for the text above.</p>\n\n <p>Another closing paragraph of a previous content block, again flush against\n the heading that follows it in the document flow.</p>\n <h2 class=\"flag-crowded\">Flag Crowded Two</h2>\n <p>Second crowded case. Two or more headings must violate the principle before\n the rule fires, because a single occurrence is treated as noise.</p>\n\n <p>A third closing paragraph that the next heading will visually attach to,\n which is exactly the inversion this rule measures.</p>\n <h3 class=\"flag-crowded\">Flag Crowded Three</h3>\n <p>Third crowded case, this time an h3, to confirm the rule reads h2 through h4.</p>\n\n <h2 class=\"pass-generous\">Pass Generous Above</h2>\n <p>Sixty-four pixels above and sixteen below. The heading clearly binds to this\n paragraph, which is the healthy shape.</p>\n\n <h2 class=\"pass-near-equal\">Pass Near Equal</h2>\n <p>Twenty pixels above and twenty-four below. Ambiguous rather than inverted,\n so the rule leaves it alone.</p>\n\n <p class=\"pass-eyebrow-label\">Chapter Four</p>\n <h2 class=\"pass-eyebrow-heading\">Pass Eyebrow Cluster</h2>\n <p>The tiny label above belongs to the heading cluster. Space is measured from\n the top of the cluster, where seventy-two pixels separate it from this text.</p>\n\n <div class=\"pass-card\">\n <h3>Pass Card Heading</h3>\n <p>A heading inside a small card. The card boundary provides the separation,\n so the rule exempts headings inside panels shorter than two hundred pixels.</p>\n </div>\n\n <div class=\"pass-band\">\n <h2>Pass Band Heading</h2>\n <p>This heading is the first content inside a section that draws its own\n background, so the band edge is the separator above it.</p>\n </div>\n\n</body>\n</html>\n",{"styleText":"\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: Georgia, serif; font-size: 16px; line-height: 1.5; width: 720px; }\n h2 { font-size: 28px; line-height: 1.2; }\n h3 { font-size: 20px; line-height: 1.2; }\n p { max-width: 60ch; }\n\n /* FLAG: zero space above the heading, clear space below it */\n .flag-crowded { margin: 0 0 40px; }\n .flag-crowded + p { margin: 0 0 24px; }\n\n /* PASS: generous space above, tight below */\n .pass-generous { margin: 64px 0 16px; }\n\n /* PASS: near-equal spacing is ambiguous, not inverted */\n .pass-near-equal { margin: 20px 0 24px; }\n\n /* PASS: eyebrow label bound to the heading; real space sits above the cluster */\n .pass-eyebrow-label { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; margin: 72px 0 8px; }\n .pass-eyebrow-heading { margin: 0 0 24px; }\n\n /* PASS: small card, its boundary provides the separation */\n .pass-card { box-shadow: 0 1px 4px rgba(0,0,0,0.2); background: #f4f4f4; padding: 16px; margin: 48px 0; width: 320px; }\n .pass-card h3 { margin: 0 0 20px; }\n\n /* PASS: heading first inside a banded section; the band edge separates */\n .pass-band { background: #ececec; padding: 8px 0 32px; margin-top: 8px; }\n .pass-band h2 { margin: 0 0 24px; }\n\nstyle=\"margin-top: 24px;\"","classText":"pass-first\nflag-crowded\nflag-crowded\nflag-crowded\npass-generous\npass-near-equal\npass-eyebrow-label\npass-eyebrow-heading\npass-card\npass-band"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Hero-Eyebrow-Chip — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n .case h1 { margin: 0; font-family: 'Inter', sans-serif; line-height: 1.05; }\n\n /* Hero size used by most cases */\n .hero { font-size: 88px; font-weight: 600; }\n\n /* ── FLAG cases ── */\n\n .eyebrow-classic {\n text-transform: uppercase;\n letter-spacing: 0.15em;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-span {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n font-size: 11px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-pill {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n background: #f1f5f9;\n color: #334155;\n border-radius: 999px;\n padding: 4px 12px;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-already-uppercase {\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #b45309;\n font-weight: 700;\n margin: 0 0 16px;\n }\n\n /* ── PASS cases ── */\n\n .pass-eyebrow-no-tracking {\n text-transform: uppercase;\n letter-spacing: normal;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .pass-body-heading {\n font-size: 24px;\n font-weight: 600;\n font-family: 'Inter', sans-serif;\n margin: 0;\n }\n\n .pass-card-caption {\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 600;\n margin: 0 0 8px;\n }\n\n .pass-bare-hero {\n font-size: 64px;\n }\n\n .pass-h2-above-h1 {\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-weight: 700;\n color: #475569;\n margin: 0 0 16px;\n font-family: 'Inter', sans-serif;\n }\n\n .pass-long-uppercase {\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n\n <div class=\"case\">\n <div class=\"eyebrow-classic\">AI-NATIVE WORKFLOWS</div>\n <h1 class=\"hero\">Eyebrow Above Hero</h1>\n <p>Classic uppercase tracked div eyebrow above an 88px hero.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-span\">NEW IN 2026</span>\n <h1 class=\"hero\">Span Eyebrow Above Hero</h1>\n <p>Span variant with the same uppercase + tracking + small-size shape.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-pill\">FEATURED</span>\n <h1 class=\"hero\">Pill Chip Above Hero</h1>\n <p>Pill-shaped chip with background, border-radius, padding.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-already-uppercase\">NEW</span>\n <h1 class=\"hero\">Already Uppercase Text</h1>\n <p>Text typed uppercase, no text-transform, but matching tracking and size.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"pass-long-uppercase\">A VERY LONG UPPERCASE TABLE OF CONTENTS HEADER</div>\n <h1 class=\"hero\">Long Uppercase Sentence Above Hero</h1>\n <p>46-char uppercase tracked label — under the 60-char eyebrow ceiling, so it still reads as an oversized eyebrow.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n\n <div class=\"case\">\n <div class=\"pass-eyebrow-no-tracking\">UPPERCASE LABEL</div>\n <h1 class=\"hero\">Eyebrow With Normal Tracking</h1>\n <p>Uppercase label above a hero but with letter-spacing: normal.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"pass-card-caption\">CARD CAPTION</div>\n <p>Plain body text below the caption.</p>\n <h1 class=\"hero\" style=\"margin-top:32px\">Uppercase Caption Far From Hero</h1>\n <p>Hero exists, but its preceding sibling is a paragraph, not the eyebrow.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-bare-hero\">Hero With No Eyebrow</h1>\n <p>Bare h1 at 64px, nothing above it.</p>\n </div>\n\n <div class=\"case\">\n <h2 class=\"pass-h2-above-h1\">SECTION HEADING</h2>\n <h1 class=\"hero\">Heading Above Heading</h1>\n <p>An h2 styled like an eyebrow above an h1 — heading-tag exclusion must skip this.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"eyebrow-classic\">SECTION KICKER</div>\n <h1 class=\"pass-body-heading\">Body-Sized Heading Below Eyebrow</h1>\n <p>A compact product/page heading is not an oversized marketing hero.</p>\n </div>\n\n <div class=\"case\" role=\"tabpanel\" aria-label=\"Saved station\">\n <div class=\"eyebrow-classic\">NEXT FROM</div>\n <h1 class=\"hero\">Application Panel Heading</h1>\n <p>Context labels inside tab panels describe application state rather than decorating a marketing hero.</p>\n </div>\n\n </div>\n\n </div>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n .case h1 { margin: 0; font-family: 'Inter', sans-serif; line-height: 1.05; }\n\n /* Hero size used by most cases */\n .hero { font-size: 88px; font-weight: 600; }\n\n /* ── FLAG cases ── */\n\n .eyebrow-classic {\n text-transform: uppercase;\n letter-spacing: 0.15em;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-span {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n font-size: 11px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-pill {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n background: #f1f5f9;\n color: #334155;\n border-radius: 999px;\n padding: 4px 12px;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-already-uppercase {\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #b45309;\n font-weight: 700;\n margin: 0 0 16px;\n }\n\n /* ── PASS cases ── */\n\n .pass-eyebrow-no-tracking {\n text-transform: uppercase;\n letter-spacing: normal;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .pass-body-heading {\n font-size: 24px;\n font-weight: 600;\n font-family: 'Inter', sans-serif;\n margin: 0;\n }\n\n .pass-card-caption {\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 600;\n margin: 0 0 8px;\n }\n\n .pass-bare-hero {\n font-size: 64px;\n }\n\n .pass-h2-above-h1 {\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-weight: 700;\n color: #475569;\n margin: 0 0 16px;\n font-family: 'Inter', sans-serif;\n }\n\n .pass-long-uppercase {\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n \nstyle=\"margin-top:32px\"","classText":"grid\ncol\ncol-label\ncase\neyebrow-classic\nhero\ncase\neyebrow-span\nhero\ncase\neyebrow-pill\nhero\ncase\neyebrow-already-uppercase\nhero\ncase\npass-long-uppercase\nhero\ncol\ncol-label\ncase\npass-eyebrow-no-tracking\nhero\ncase\npass-card-caption\nhero\ncase\npass-bare-hero\ncase\npass-h2-above-h1\nhero\ncase\neyebrow-classic\npass-body-heading\ncase\neyebrow-classic\nhero"}],"result":[]}
{"args":["<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <title>Hook inline ignore comments do not suppress detector findings</title>\n <style>\n /* impeccable: ignore overused-font */\n body {\n font-family: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n margin: 0;\n padding: 48px;\n }\n\n main {\n max-width: 62ch;\n }\n </style>\n </head>\n <body>\n <!-- impeccable: ignore overused-font -->\n <main>\n <h1>Roboto remains a detector finding</h1>\n <p>\n This fixture deliberately uses Roboto across enough visible text for\n the browser detector to report the overused-font page finding. Hook\n exceptions must live in .impeccable/hook.json instead of source comments.\n </p>\n <p>\n The comments above look like old inline hook suppressions, but they are\n intentionally inert and should not hide the finding from headless scans.\n </p>\n <ul>\n <li>Roboto usage sample one.</li>\n <li>Roboto usage sample two.</li>\n <li>Roboto usage sample three.</li>\n <li>Roboto usage sample four.</li>\n <li>Roboto usage sample five.</li>\n <li>Roboto usage sample six.</li>\n <li>Roboto usage sample seven.</li>\n <li>Roboto usage sample eight.</li>\n <li>Roboto usage sample nine.</li>\n <li>Roboto usage sample ten.</li>\n <li>Roboto usage sample eleven.</li>\n <li>Roboto usage sample twelve.</li>\n <li>Roboto usage sample thirteen.</li>\n <li>Roboto usage sample fourteen.</li>\n <li>Roboto usage sample fifteen.</li>\n <li>Roboto usage sample sixteen.</li>\n <li>Roboto usage sample seventeen.</li>\n <li>Roboto usage sample eighteen.</li>\n <li>Roboto usage sample nineteen.</li>\n <li>Roboto usage sample twenty.</li>\n </ul>\n </main>\n </body>\n</html>\n",{"styleText":"\n /* impeccable: ignore overused-font */\n body {\n font-family: \"Roboto\", ui-sans-serif, system-ui, sans-serif;\n margin: 0;\n padding: 48px;\n }\n\n main {\n max-width: 62ch;\n }\n ","classText":""}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Icon-Tile-Stack — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h3> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }\n .case p { font-size: 14px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */\n .icon-pastel {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 2: solid-color icon-tile, white svg inside (no gradient) */\n .icon-solid {\n width: 64px; height: 64px;\n border-radius: 16px;\n background: #3b82f6;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 3: white-bg outlined icon-tile (no chromatic background at all) */\n .icon-white {\n width: 72px; height: 72px;\n border-radius: 18px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 4: larger square (still inside the 32128 range) */\n .icon-large {\n width: 96px; height: 96px;\n border-radius: 24px;\n background: #f1f5f9;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 5: inline emoji icon — tile contains an emoji/symbol character directly,\n not an <svg> or <i> child. This is the \"card-icon\" pattern from many\n generated demos. */\n .icon-emoji {\n width: 48px; height: 48px;\n border-radius: 12px;\n background: linear-gradient(135deg, #8b5cf6, #a855f7);\n display: flex; align-items: center; justify-content: center;\n font-size: 24px;\n margin-bottom: 16px;\n }\n\n /* ── PASS cases ── */\n\n /* a: round avatar above name (border-radius: 50% — circle, not rounded square) */\n .avatar-round {\n width: 64px; height: 64px;\n border-radius: 50%;\n background: #f59e0b;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* b: wide thumbnail above article title (16:9, much wider than tall) */\n .thumb-wide {\n width: 200px; height: 112px;\n border-radius: 8px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n\n /* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */\n .inline-row { display: flex; align-items: center; gap: 12px; }\n .inline-row .icon-inline {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: #ddd6fe;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n }\n\n /* d: nothing above the heading at all */\n .no-icon h3 { margin-top: 0; }\n\n /* e: icon-tile that's too small (under 32px) — out of size range */\n .icon-tiny {\n width: 24px; height: 24px;\n border-radius: 6px;\n background: #93c5fd;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* f: oversized image card / hero image (over 128px) — out of size range */\n .img-huge {\n width: 200px; height: 200px;\n border-radius: 16px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <div class=\"case\">\n <div class=\"icon-pastel\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3b82f6\" stroke-width=\"1.5\">\n <polyline points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n </div>\n <h3>Lightning Fast</h3>\n <p>Built for speed and performance from the ground up.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-solid\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ffffff\" stroke-width=\"1.5\">\n <path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/>\n </svg>\n </div>\n <h3>Secure Storage</h3>\n <p>Enterprise-grade security for your peace of mind.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-white\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0f172a\" stroke-width=\"1.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"M12 6v6l4 2\"/>\n </svg>\n </div>\n <h3>Easy Setup</h3>\n <p>Get started in minutes with zero configuration.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-large\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0f172a\" stroke-width=\"1.5\">\n <line x1=\"18\" y1=\"20\" x2=\"18\" y2=\"10\"/>\n <line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/>\n <line x1=\"6\" y1=\"20\" x2=\"6\" y2=\"14\"/>\n </svg>\n </div>\n <h3>Powerful Analytics</h3>\n <p>Insights that drive smarter business decisions.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-emoji\">&#9889;</div>\n <h3>Emoji Inline Icon</h3>\n <p>The tile contains an emoji directly, no SVG child.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <!-- a: round avatar (circle, not rounded square) -->\n <div class=\"case\">\n <div class=\"avatar-round\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/>\n <path d=\"M4 22v-2a8 8 0 0116 0v2\"/>\n </svg>\n </div>\n <h3>Sarah Chen</h3>\n <p>Product designer based in Berlin.</p>\n </div>\n\n <!-- b: wide 16:9 thumbnail (aspect ratio out of range) -->\n <div class=\"case\">\n <div class=\"thumb-wide\"></div>\n <h3>Article Headline</h3>\n <p>A summary of an interesting article.</p>\n </div>\n\n <!-- c: side-by-side icon and heading (not stacked vertically) -->\n <div class=\"case\">\n <div class=\"inline-row\">\n <div class=\"icon-inline\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#7c3aed\" stroke-width=\"1.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n </svg>\n </div>\n <div>\n <h3>Inline Side By Side</h3>\n <p>Icon sits next to the heading, not above it.</p>\n </div>\n </div>\n </div>\n\n <!-- d: no icon above the heading at all -->\n <div class=\"case no-icon\">\n <h3>Plain Heading No Icon</h3>\n <p>Just a heading with body text, nothing above.</p>\n </div>\n\n <!-- e: icon-tile too small (24px — under 32px floor) -->\n <div class=\"case\">\n <div class=\"icon-tiny\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#1e40af\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n </svg>\n </div>\n <h3>Tiny Icon Above Me</h3>\n <p>The icon container is smaller than 32px.</p>\n </div>\n\n <!-- f: huge image / hero (200px — over 128px ceiling) -->\n <div class=\"case\">\n <div class=\"img-huge\"></div>\n <h3>Huge Hero Image</h3>\n <p>The container is larger than the size cap.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h3> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }\n .case p { font-size: 14px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */\n .icon-pastel {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 2: solid-color icon-tile, white svg inside (no gradient) */\n .icon-solid {\n width: 64px; height: 64px;\n border-radius: 16px;\n background: #3b82f6;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 3: white-bg outlined icon-tile (no chromatic background at all) */\n .icon-white {\n width: 72px; height: 72px;\n border-radius: 18px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 4: larger square (still inside the 32128 range) */\n .icon-large {\n width: 96px; height: 96px;\n border-radius: 24px;\n background: #f1f5f9;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 5: inline emoji icon — tile contains an emoji/symbol character directly,\n not an <svg> or <i> child. This is the \"card-icon\" pattern from many\n generated demos. */\n .icon-emoji {\n width: 48px; height: 48px;\n border-radius: 12px;\n background: linear-gradient(135deg, #8b5cf6, #a855f7);\n display: flex; align-items: center; justify-content: center;\n font-size: 24px;\n margin-bottom: 16px;\n }\n\n /* ── PASS cases ── */\n\n /* a: round avatar above name (border-radius: 50% — circle, not rounded square) */\n .avatar-round {\n width: 64px; height: 64px;\n border-radius: 50%;\n background: #f59e0b;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* b: wide thumbnail above article title (16:9, much wider than tall) */\n .thumb-wide {\n width: 200px; height: 112px;\n border-radius: 8px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n\n /* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */\n .inline-row { display: flex; align-items: center; gap: 12px; }\n .inline-row .icon-inline {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: #ddd6fe;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n }\n\n /* d: nothing above the heading at all */\n .no-icon h3 { margin-top: 0; }\n\n /* e: icon-tile that's too small (under 32px) — out of size range */\n .icon-tiny {\n width: 24px; height: 24px;\n border-radius: 6px;\n background: #93c5fd;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* f: oversized image card / hero image (over 128px) — out of size range */\n .img-huge {\n width: 200px; height: 200px;\n border-radius: 16px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n ","classText":"grid\ncol\ncase\nicon-pastel\ncase\nicon-solid\ncase\nicon-white\ncase\nicon-large\ncase\nicon-emoji\ncol\ncase\navatar-round\ncase\nthumb-wide\ncase\ninline-row\nicon-inline\ncase no-icon\ncase\nicon-tiny\ncase\nimg-huge"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":{"$undef":true}}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Fixture: image-backed text sampled at the pixel level by default</title>\n<style>\n body { margin: 0; padding: 40px; background: #17150f; font-family: Arial, sans-serif; }\n /* A same-origin (data-URI) near-white image background. The analytic walk\n cannot know its pixels, so the default overlay pass must sample the image\n itself: white text on it fails, dark ink on it passes. */\n .photo-panel {\n width: 420px;\n padding: 24px;\n margin: 0 0 24px;\n background: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'><rect width='100' height='100' fill='%23f5f2ea'/></svg>\") center / cover no-repeat;\n }\n .flag-white-on-light { color: #fdfdfd; font-size: 16px; line-height: 1.6; margin: 0; }\n .pass-ink-on-light { color: #262421; font-size: 16px; line-height: 1.6; margin: 0; }\n /* Fourteen gradient-backed decoys sit BEFORE the photo panels in DOM\n order. They are visual-contrast candidates (gradient background) but not\n image-backed; with the default 12-candidate cap they would starve the\n image-only pass to zero unless the mode filters inside the collector.\n Their ink is dark on a light gradient, so no analytic finding fires. */\n .decoy { display: inline-block; padding: 4px 8px; margin: 0 4px 6px 0; background: linear-gradient(180deg, #f2efe6, #e8e4d8); color: #24211c; font-size: 12px; }\n</style>\n</head>\n<body>\n <div><span class=\"decoy-row\"></span>\n <div class=\"decoy\">Decoy 1</div>\n <div class=\"decoy\">Decoy 2</div>\n <div class=\"decoy\">Decoy 3</div>\n <div class=\"decoy\">Decoy 4</div>\n <div class=\"decoy\">Decoy 5</div>\n <div class=\"decoy\">Decoy 6</div>\n <div class=\"decoy\">Decoy 7</div>\n <div class=\"decoy\">Decoy 8</div>\n <div class=\"decoy\">Decoy 9</div>\n <div class=\"decoy\">Decoy 10</div>\n <div class=\"decoy\">Decoy 11</div>\n <div class=\"decoy\">Decoy 12</div>\n <div class=\"decoy\">Decoy 13</div>\n <div class=\"decoy\">Decoy 14</div>\n </div>\n <div class=\"photo-panel\">\n <p class=\"flag-white-on-light\">White text on a near-white image background is unreadable, and only pixel sampling can prove it.</p>\n </div>\n <div class=\"photo-panel\">\n <p class=\"pass-ink-on-light\">Dark ink on the same image background reads fine and must not flag.</p>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { margin: 0; padding: 40px; background: #17150f; font-family: Arial, sans-serif; }\n /* A same-origin (data-URI) near-white image background. The analytic walk\n cannot know its pixels, so the default overlay pass must sample the image\n itself: white text on it fails, dark ink on it passes. */\n .photo-panel {\n width: 420px;\n padding: 24px;\n margin: 0 0 24px;\n background: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'><rect width='100' height='100' fill='%23f5f2ea'/></svg>\") center / cover no-repeat;\n }\n .flag-white-on-light { color: #fdfdfd; font-size: 16px; line-height: 1.6; margin: 0; }\n .pass-ink-on-light { color: #262421; font-size: 16px; line-height: 1.6; margin: 0; }\n /* Fourteen gradient-backed decoys sit BEFORE the photo panels in DOM\n order. They are visual-contrast candidates (gradient background) but not\n image-backed; with the default 12-candidate cap they would starve the\n image-only pass to zero unless the mode filters inside the collector.\n Their ink is dark on a light gradient, so no analytic finding fires. */\n .decoy { display: inline-block; padding: 4px 8px; margin: 0 4px 6px 0; background: linear-gradient(180deg, #f2efe6, #e8e4d8); color: #24211c; font-size: 12px; }\n","classText":"decoy-row\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\ndecoy\nphoto-panel\nflag-white-on-light\nphoto-panel\npass-ink-on-light"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Italic-Serif-Display — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1>/<h2>/<blockquote>\n text so the test can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: Fraunces 88px italic — the Lumina hero pattern */\n .hero-fraunces {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 88px;\n font-weight: 400;\n line-height: 1.05;\n margin: 0;\n }\n\n /* 2: Recoleta 64px italic */\n .hero-recoleta {\n font-family: 'Recoleta', serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 400;\n margin: 0;\n }\n\n /* 3: Playfair Display 72px italic */\n .hero-playfair {\n font-family: 'Playfair Display', serif;\n font-style: italic;\n font-size: 72px;\n font-weight: 500;\n margin: 0;\n }\n\n /* 4: Unknown serif primary, but generic `serif` fallback in stack */\n .hero-unknown-serif {\n font-family: 'CustomSerifFace', serif;\n font-style: italic;\n font-size: 56px;\n font-weight: 400;\n margin: 0;\n }\n\n /* ── PASS cases ── */\n\n /* a: italic, but sans-serif primary (Karla) — italic display in a sans is rare and usually intentional */\n .pass-sans-italic {\n font-family: 'Karla', sans-serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 600;\n margin: 0;\n }\n\n /* b: serif at 88px, but font-style: normal */\n .pass-roman-serif {\n font-family: 'Fraunces', Georgia, serif;\n font-style: normal;\n font-size: 88px;\n font-weight: 600;\n margin: 0;\n }\n\n /* c: italic serif blockquote at 24px (genuine pull quote, not a hero) */\n .pass-pull-quote {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 24px;\n font-weight: 400;\n margin: 0;\n color: #334155;\n }\n\n /* d: roman h1 with an italic <em> inside — v1 explicitly does NOT flag this */\n .pass-roman-h1-em-italic {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 72px;\n font-weight: 600;\n margin: 0;\n }\n .pass-roman-h1-em-italic em {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-weight: 400;\n }\n\n /* e: italic serif h2 at 32px — under the 48px hero threshold */\n .pass-italic-serif-32 {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 32px;\n font-weight: 400;\n margin: 0;\n }\n\n /* f: baseline-clean h1 — sans-serif roman */\n .pass-h1-sans-roman {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 64px;\n font-weight: 700;\n margin: 0;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n\n <div class=\"case\">\n <h1 class=\"hero-fraunces\">Fraunces 88px italic</h1>\n <p>The Lumina hero pattern: oversized Fraunces italic display.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-recoleta\">Recoleta 64px italic</h1>\n <p>Recoleta italic at 64px, generic serif fallback.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-playfair\">Playfair 72px italic</h1>\n <p>Playfair Display italic at hero scale.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-unknown-serif\">Unknown Serif Generic Fallback</h1>\n <p>Primary font unknown to the detector, but the stack ends in generic serif.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n\n <div class=\"case\">\n <h1 class=\"pass-sans-italic\">Sans Italic Display</h1>\n <p>Italic at hero scale, but in a sans-serif face.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-roman-serif\">Roman Serif Display</h1>\n <p>Fraunces at 88px, but font-style is normal (not italic).</p>\n </div>\n\n <div class=\"case\">\n <blockquote class=\"pass-pull-quote\">Italic Serif Pull Quote</blockquote>\n <p>Italic serif at 24px is a legitimate pull-quote pattern, not a hero.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-roman-h1-em-italic\">Inline Em Inside <em>Roman</em></h1>\n <p>Roman h1 with an inline italic em — v1 deliberately does not flag this.</p>\n </div>\n\n <div class=\"case\">\n <h2 class=\"pass-italic-serif-32\">Italic Serif at 32px</h2>\n <p>Italic serif h2 below the 48px hero threshold.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-h1-sans-roman\">h1 Sans-Serif Roman</h1>\n <p>Baseline-clean control: sans-serif, roman, hero scale.</p>\n </div>\n </div>\n\n </div>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1>/<h2>/<blockquote>\n text so the test can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: Fraunces 88px italic — the Lumina hero pattern */\n .hero-fraunces {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 88px;\n font-weight: 400;\n line-height: 1.05;\n margin: 0;\n }\n\n /* 2: Recoleta 64px italic */\n .hero-recoleta {\n font-family: 'Recoleta', serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 400;\n margin: 0;\n }\n\n /* 3: Playfair Display 72px italic */\n .hero-playfair {\n font-family: 'Playfair Display', serif;\n font-style: italic;\n font-size: 72px;\n font-weight: 500;\n margin: 0;\n }\n\n /* 4: Unknown serif primary, but generic `serif` fallback in stack */\n .hero-unknown-serif {\n font-family: 'CustomSerifFace', serif;\n font-style: italic;\n font-size: 56px;\n font-weight: 400;\n margin: 0;\n }\n\n /* ── PASS cases ── */\n\n /* a: italic, but sans-serif primary (Karla) — italic display in a sans is rare and usually intentional */\n .pass-sans-italic {\n font-family: 'Karla', sans-serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 600;\n margin: 0;\n }\n\n /* b: serif at 88px, but font-style: normal */\n .pass-roman-serif {\n font-family: 'Fraunces', Georgia, serif;\n font-style: normal;\n font-size: 88px;\n font-weight: 600;\n margin: 0;\n }\n\n /* c: italic serif blockquote at 24px (genuine pull quote, not a hero) */\n .pass-pull-quote {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 24px;\n font-weight: 400;\n margin: 0;\n color: #334155;\n }\n\n /* d: roman h1 with an italic <em> inside — v1 explicitly does NOT flag this */\n .pass-roman-h1-em-italic {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 72px;\n font-weight: 600;\n margin: 0;\n }\n .pass-roman-h1-em-italic em {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-weight: 400;\n }\n\n /* e: italic serif h2 at 32px — under the 48px hero threshold */\n .pass-italic-serif-32 {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 32px;\n font-weight: 400;\n margin: 0;\n }\n\n /* f: baseline-clean h1 — sans-serif roman */\n .pass-h1-sans-roman {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 64px;\n font-weight: 700;\n margin: 0;\n }\n ","classText":"grid\ncol\ncol-label\ncase\nhero-fraunces\ncase\nhero-recoleta\ncase\nhero-playfair\ncase\nhero-unknown-serif\ncol\ncol-label\ncase\npass-sans-italic\ncase\npass-roman-serif\ncase\npass-pull-quote\ncase\npass-roman-h1-em-italic\ncase\npass-italic-serif-32\ncase\npass-h1-sans-roman"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Kicker Above Heading Fixture</title>\n <style>\n body {\n margin: 0;\n font-family: Arial, sans-serif;\n background: #f6f2ec;\n color: #171411;\n }\n main {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n padding: 32px;\n }\n .col {\n min-width: 0;\n border: 1px solid #c9bfb2;\n padding: 24px;\n }\n .case {\n min-height: 120px;\n margin: 0 0 24px;\n padding: 16px;\n background: #fffaf3;\n }\n .kicker,\n .pass-kicker,\n .pass-kicker-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n color: #5b5046;\n }\n .kicker,\n .pass-kicker-caps {\n text-transform: uppercase;\n }\n /* The wild's most common kicker recipe: 0.08em tracking at a sub-13px\n size computes to under 1px, which the old absolute floor excluded. */\n .tight-tracked-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .smallcaps-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.12em;\n font-variant: small-caps;\n color: #5b5046;\n }\n .untracked-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: normal;\n text-transform: uppercase;\n color: #5b5046;\n }\n h1,\n h2 {\n margin: 0 0 12px;\n font-size: 32px;\n line-height: 38px;\n font-weight: 700;\n }\n h3 {\n margin: 0 0 12px;\n font-size: 24px;\n line-height: 30px;\n font-weight: 700;\n }\n h4 {\n margin: 0 0 12px;\n font-size: 20px;\n line-height: 26px;\n font-weight: 700;\n }\n .role-heading {\n margin: 0 0 12px;\n font-size: 28px;\n line-height: 34px;\n font-weight: 700;\n }\n .hero-h1 {\n margin: 0 0 12px;\n font-size: 64px;\n line-height: 70px;\n font-weight: 700;\n }\n .hero-eyebrow {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #5b5046;\n }\n p,\n label,\n figcaption,\n a,\n li {\n font-size: 16px;\n line-height: 24px;\n }\n nav,\n form,\n figure,\n ol {\n margin: 0 0 24px;\n padding: 16px;\n border: 1px solid #d8cec0;\n min-height: 80px;\n }\n .card-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 0 0 24px;\n }\n .suite-card {\n display: block;\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .taxonomy-label {\n display: block;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .suite-card h3 {\n font-size: 24px;\n line-height: 30px;\n }\n .stat {\n min-height: 100px;\n padding: 16px;\n background: #fffaf3;\n }\n .stat h3 {\n font-size: 40px;\n line-height: 46px;\n }\n .hidden-mockup {\n display: grid;\n gap: 12px;\n margin: 0 0 24px;\n }\n .mockup-hero {\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .mockup-hero h2 {\n font-size: 24px;\n line-height: 30px;\n }\n </style>\n</head>\n<body>\n <main>\n <section class=\"col\" aria-label=\"Should flag\">\n <h1>Should flag</h1>\n\n <section class=\"case\">\n <span class=\"kicker\">Invitation protocol</span>\n <h2>\"A Single Kicker Still Flags\"</h2>\n <p>One tracked uppercase label above one heading is enough. No repetition required.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"tight-tracked-kicker\">Output model</span>\n <h2>\"Standard Tracking Kicker\"</h2>\n <p>Uppercase at 12px with 0.08em tracking is the most common authored kicker and must flag.</p>\n </section>\n\n <section class=\"case\">\n <p class=\"kicker\">Preview sequence</p>\n <h3>\"Kicker Above An H3\"</h3>\n <p>Subsection headings get no eyebrow either.</p>\n </section>\n\n <section class=\"case\">\n <div class=\"kicker\">Access window</div>\n <h4>\"Kicker Above An H4\"</h4>\n <p>The ban runs down to h4.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"kicker\">Platform notes</span>\n <h1 class=\"sub-hero\">\"Sub Hero Heading\"</h1>\n <p>A 32px h1 is below hero scale, so this pair belongs to the general ban.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"smallcaps-kicker\">materials briefing</span>\n <h2>\"Small Caps Kicker\"</h2>\n <p>Small-caps styling is the same label in a different costume.</p>\n </section>\n\n <section class=\"case\">\n <div class=\"kicker\">Field studies</div>\n <div role=\"heading\" aria-level=\"2\" class=\"role-heading\">\"Heading Role Kicker\"</div>\n <p>Elements carrying a heading role are headings for this rule.</p>\n </section>\n </section>\n\n <section class=\"col\" aria-label=\"Should pass\">\n <h1>Should pass</h1>\n\n <nav class=\"case\" aria-label=\"Breadcrumb\">\n <span class=\"pass-kicker\">Home / Journal</span>\n <h2>\"Breadcrumb Before Heading\"</h2>\n <a href=\"/\">Home</a>\n </nav>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Home / Journal / Archive</span>\n <h2>\"Breadcrumb Trail Outside Nav\"</h2>\n <p>A path with separators is wayfinding, not a kicker, even without a nav wrapper.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">News · Jan 12, 2026</span>\n <h2>\"Dateline Above Headline\"</h2>\n <p>Editorial category-and-date meta lines are content metadata.</p>\n </section>\n\n <article class=\"case\">\n <span class=\"pass-kicker-caps\">Field report</span>\n <h3>\"Editorial Card Meta\"</h3>\n <p>Category labels inside an article card are structured metadata for scanning.</p>\n </article>\n\n <form class=\"case\">\n <label class=\"pass-kicker\" for=\"guest\">Guest name</label>\n <input id=\"guest\" name=\"guest\" style=\"width: 220px; height: 40px;\">\n <h2>\"Form Heading Is Separate\"</h2>\n </form>\n\n <figure class=\"case\">\n <figcaption class=\"pass-kicker-caps\">Plate study</figcaption>\n <h3>\"Figure Caption Label\"</h3>\n </figure>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Section 4.2</span>\n <h2>\"Limitation Of Liability\"</h2>\n <p>Legal and document numbering identifies the clause; it is not decoration.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">§ 12.3</span>\n <h2>\"Indemnification Clause\"</h2>\n <p>Statutory references keep their marker.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Chapter Four</span>\n <h2>\"Chapter Numbering Passes\"</h2>\n <p>Spelled-out chapter numbering is document structure, like legal numbering.</p>\n </section>\n\n <section class=\"case\" role=\"tabpanel\" aria-label=\"Saved station\">\n <span class=\"pass-kicker-caps\">Next from</span>\n <h2>\"Application Panel Context\"</h2>\n <p>Context labels inside application panels describe state, not decoration.</p>\n </section>\n\n <section class=\"case\">\n <nav aria-label=\"Primary\"><a href=\"/\">Home</a> <a href=\"/docs\">Docs</a></nav>\n <h1>\"Page Title After Nav\"</h1>\n <p>A navigation landmark preceding the page title is chrome, not a kicker.</p>\n </section>\n\n <div class=\"stat\">\n <h3>\"48%\"</h3>\n <span class=\"pass-kicker-caps\">Growth yoy</span>\n <p>Stat callouts put the small label below the number. That direction is legal.</p>\n </div>\n\n <section class=\"case\">\n <span class=\"pass-kicker\">A quiet lead-in sentence</span>\n <h2>\"Sentence Case Lead In\"</h2>\n <p>Not uppercase, not small-caps: an ordinary standfirst.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"untracked-caps\">New arrivals</span>\n <h2>\"Untracked Caps Label\"</h2>\n <p>Uppercase without tracking lacks the kicker styling signature.</p>\n </section>\n\n <section class=\"case\" data-impeccable-allow-kickers>\n <span class=\"pass-kicker-caps\">Archive code</span>\n <h2>\"Intentional Brand Label\"</h2>\n <p>Deliberate brand systems can opt out with an explicit marker.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"hero-eyebrow\">Platform overview</span>\n <h1 class=\"hero-h1\">\"Hero Owned By Hero Rule\"</h1>\n <p>Hero-scale h1 eyebrows stay with hero-eyebrow-chip; this rule stands down.</p>\n </section>\n\n <div class=\"card-grid\" aria-label=\"Room cards\">\n <article class=\"suite-card\">\n <span class=\"taxonomy-label\">Suite</span>\n <h3>\"Garden Suite\"</h3>\n <p>Category labels inside sibling cards are metadata for comparison.</p>\n </article>\n <article class=\"suite-card\">\n <span class=\"taxonomy-label\">Suite</span>\n <h3>\"Sea Loft\"</h3>\n <p>The label is useful because the user is scanning sibling cards.</p>\n </article>\n </div>\n\n <ol class=\"case\">\n <li>\n <span class=\"pass-kicker-caps\">Step 01</span>\n <h3>\"Step Indicator\"</h3>\n <p>Step indicators in ordered flows are sequence markers.</p>\n </li>\n </ol>\n\n <div class=\"hidden-mockup\" aria-hidden=\"true\">\n <div class=\"mockup-hero\">\n <span class=\"taxonomy-label\">Amalfi Coast</span>\n <h2>\"Mockup Hero Variant One\"</h2>\n </div>\n </div>\n </section>\n </main>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n font-family: Arial, sans-serif;\n background: #f6f2ec;\n color: #171411;\n }\n main {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n padding: 32px;\n }\n .col {\n min-width: 0;\n border: 1px solid #c9bfb2;\n padding: 24px;\n }\n .case {\n min-height: 120px;\n margin: 0 0 24px;\n padding: 16px;\n background: #fffaf3;\n }\n .kicker,\n .pass-kicker,\n .pass-kicker-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n color: #5b5046;\n }\n .kicker,\n .pass-kicker-caps {\n text-transform: uppercase;\n }\n /* The wild's most common kicker recipe: 0.08em tracking at a sub-13px\n size computes to under 1px, which the old absolute floor excluded. */\n .tight-tracked-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .smallcaps-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.12em;\n font-variant: small-caps;\n color: #5b5046;\n }\n .untracked-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: normal;\n text-transform: uppercase;\n color: #5b5046;\n }\n h1,\n h2 {\n margin: 0 0 12px;\n font-size: 32px;\n line-height: 38px;\n font-weight: 700;\n }\n h3 {\n margin: 0 0 12px;\n font-size: 24px;\n line-height: 30px;\n font-weight: 700;\n }\n h4 {\n margin: 0 0 12px;\n font-size: 20px;\n line-height: 26px;\n font-weight: 700;\n }\n .role-heading {\n margin: 0 0 12px;\n font-size: 28px;\n line-height: 34px;\n font-weight: 700;\n }\n .hero-h1 {\n margin: 0 0 12px;\n font-size: 64px;\n line-height: 70px;\n font-weight: 700;\n }\n .hero-eyebrow {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #5b5046;\n }\n p,\n label,\n figcaption,\n a,\n li {\n font-size: 16px;\n line-height: 24px;\n }\n nav,\n form,\n figure,\n ol {\n margin: 0 0 24px;\n padding: 16px;\n border: 1px solid #d8cec0;\n min-height: 80px;\n }\n .card-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 0 0 24px;\n }\n .suite-card {\n display: block;\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .taxonomy-label {\n display: block;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .suite-card h3 {\n font-size: 24px;\n line-height: 30px;\n }\n .stat {\n min-height: 100px;\n padding: 16px;\n background: #fffaf3;\n }\n .stat h3 {\n font-size: 40px;\n line-height: 46px;\n }\n .hidden-mockup {\n display: grid;\n gap: 12px;\n margin: 0 0 24px;\n }\n .mockup-hero {\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .mockup-hero h2 {\n font-size: 24px;\n line-height: 30px;\n }\n \nstyle=\"width: 220px; height: 40px;\"","classText":"col\ncase\nkicker\ncase\ntight-tracked-kicker\ncase\nkicker\ncase\nkicker\ncase\nkicker\nsub-hero\ncase\nsmallcaps-kicker\ncase\nkicker\nrole-heading\ncol\ncase\npass-kicker\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\nstat\npass-kicker-caps\ncase\npass-kicker\ncase\nuntracked-caps\ncase\npass-kicker-caps\ncase\nhero-eyebrow\nhero-h1\ncard-grid\nsuite-card\ntaxonomy-label\nsuite-card\ntaxonomy-label\ncase\npass-kicker-caps\nhidden-mockup\nmockup-hero\ntaxonomy-label"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Layout Anti-Patterns — Should Flag vs Should Pass</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; color: #111827; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Nested cards (cardocalypse)</h3>\n\n <!-- Classic: card inside card -->\n <div class=\"bg-white rounded-lg shadow-md p-6 mb-4\">\n <h4 class=\"text-lg font-semibold mb-3\">Outer Card</h4>\n <div class=\"bg-white rounded-lg shadow-md p-4\">\n <h5 class=\"font-medium\">Inner Card</h5>\n <p class=\"text-sm text-gray-600\">Card inside card.</p>\n </div>\n </div>\n\n <!-- Three levels deep -->\n <div class=\"bg-white rounded-xl shadow-lg p-6 mb-4\">\n <h4 class=\"text-lg font-semibold mb-3\">Level 1</h4>\n <div class=\"bg-gray-50 rounded-lg shadow-sm p-4 mb-3\">\n <h5 class=\"font-medium mb-2\">Level 2</h5>\n <div class=\"bg-white rounded-md shadow-sm p-3\">\n <p class=\"text-sm\">Level 3 — nesting inception.</p>\n </div>\n </div>\n </div>\n\n <!-- CSS variant: border + bg + shadow nesting -->\n <div style=\"background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 1.5rem; margin-bottom: 1rem;\">\n <h4 style=\"font-weight: 600; margin-bottom: 0.75rem;\">Outer (CSS)</h4>\n <div style=\"background: #f9fafb; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 1rem;\">\n <p style=\"font-size: 0.875rem; color: #6b7280;\">Inner card via CSS.</p>\n </div>\n </div>\n\n <!-- shadcn-style Card nested in Card -->\n <div class=\"rounded-lg border bg-white shadow-sm p-6 mb-4\" data-component=\"card\">\n <h4 class=\"font-semibold mb-3\">shadcn-style Outer Card</h4>\n <div class=\"rounded-lg border bg-white shadow-sm p-4\" data-component=\"card\">\n <p class=\"text-sm text-gray-600\">Another shadcn Card nested inside.</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>shadcn Card sub-components (not nested cards)</h3>\n <div class=\"rounded-lg border bg-white shadow-sm mb-4\">\n <div class=\"flex flex-col space-y-1.5 p-6\">\n <h4 class=\"text-lg font-semibold\">Card Title</h4>\n <p class=\"text-sm text-gray-500\">Card description goes here.</p>\n </div>\n <div class=\"p-6 pt-0\">\n <p class=\"text-sm text-gray-600\">CardContent — a sub-section, not a nested card. No shadow, no border-radius of its own.</p>\n </div>\n <div class=\"flex items-center p-6 pt-0\">\n <button class=\"px-4 py-2 bg-gray-900 text-white text-sm rounded-md\">Action</button>\n </div>\n </div>\n\n <h3>Card with form inputs</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-4\">Settings</h4>\n <div class=\"space-y-3\">\n <input type=\"text\" placeholder=\"Name\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm\">\n <input type=\"email\" placeholder=\"Email\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm\">\n <select class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white\">\n <option>Select option</option>\n </select>\n <textarea placeholder=\"Bio\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none\"></textarea>\n </div>\n </div>\n\n <h3>Card with code block</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-3\">Installation</h4>\n <pre class=\"bg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto\"><code>npm install @acme/sdk</code></pre>\n </div>\n\n <h3>Card with badges</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-3\">Tags</h4>\n <div class=\"flex flex-wrap gap-2\">\n <span class=\"px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium\">React</span>\n <span class=\"px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium\">TypeScript</span>\n <span class=\"px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium\">Tailwind</span>\n </div>\n </div>\n\n <h3>Card with image</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <div class=\"w-full h-32 bg-gradient-to-br from-amber-400 to-rose-500 rounded-lg shadow-inner mb-4\"></div>\n <h4 class=\"font-semibold\">Project Preview</h4>\n <p class=\"text-sm text-gray-600 mt-1\">Image with rounded corners and shadow.</p>\n </div>\n\n <h3>Pricing cards (intentionally similar)</h3>\n <div class=\"grid grid-cols-3 gap-2 mb-4\">\n <div class=\"bg-white rounded-lg shadow-sm p-4 border\">\n <h4 class=\"font-semibold mb-1 text-sm\">Free</h4>\n <div class=\"text-lg font-bold mb-2\">$0</div>\n </div>\n <div class=\"bg-white rounded-lg shadow-sm p-4 border-2 border-blue-500\">\n <h4 class=\"font-semibold mb-1 text-sm\">Pro</h4>\n <div class=\"text-lg font-bold mb-2\">$29</div>\n </div>\n <div class=\"bg-white rounded-lg shadow-sm p-4 border\">\n <h4 class=\"font-semibold mb-1 text-sm\">Enterprise</h4>\n <div class=\"text-lg font-bold mb-2\">Custom</div>\n </div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; color: #111827; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n \nstyle=\"background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 1.5rem; margin-bottom: 1rem;\"\nstyle=\"font-weight: 600; margin-bottom: 0.75rem;\"\nstyle=\"background: #f9fafb; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 1rem;\"\nstyle=\"font-size: 0.875rem; color: #6b7280;\"","classText":"grid\ncol\nbg-white rounded-lg shadow-md p-6 mb-4\ntext-lg font-semibold mb-3\nbg-white rounded-lg shadow-md p-4\nfont-medium\ntext-sm text-gray-600\nbg-white rounded-xl shadow-lg p-6 mb-4\ntext-lg font-semibold mb-3\nbg-gray-50 rounded-lg shadow-sm p-4 mb-3\nfont-medium mb-2\nbg-white rounded-md shadow-sm p-3\ntext-sm\nrounded-lg border bg-white shadow-sm p-6 mb-4\nfont-semibold mb-3\nrounded-lg border bg-white shadow-sm p-4\ntext-sm text-gray-600\ncol\nrounded-lg border bg-white shadow-sm mb-4\nflex flex-col space-y-1.5 p-6\ntext-lg font-semibold\ntext-sm text-gray-500\np-6 pt-0\ntext-sm text-gray-600\nflex items-center p-6 pt-0\npx-4 py-2 bg-gray-900 text-white text-sm rounded-md\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-4\nspace-y-3\nw-full px-3 py-2 border rounded-md shadow-sm text-sm\nw-full px-3 py-2 border rounded-md shadow-sm text-sm\nw-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white\nw-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-3\nbg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-3\nflex flex-wrap gap-2\npx-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium\npx-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium\npx-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nw-full h-32 bg-gradient-to-br from-amber-400 to-rose-500 rounded-lg shadow-inner mb-4\nfont-semibold\ntext-sm text-gray-600 mt-1\ngrid grid-cols-3 gap-2 mb-4\nbg-white rounded-lg shadow-sm p-4 border\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2\nbg-white rounded-lg shadow-sm p-4 border-2 border-blue-500\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2\nbg-white rounded-lg shadow-sm p-4 border\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Legitimate Border Patterns — Should NOT Flag</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }\n .demo { max-width: 36rem; margin-bottom: 1.5rem; }\n </style>\n</head>\n<body>\n <h1>Legitimate Border Patterns</h1>\n <p style=\"color: #6b7280; margin-bottom: 2rem;\">Every border here is a well-established web pattern. None should be flagged.</p>\n\n <!-- 1. BLOCKQUOTE — the classic border-left usage -->\n <h2>Blockquotes</h2>\n <div class=\"demo\">\n <blockquote style=\"border-left: 4px solid #d1d5db; padding-left: 1rem; margin: 0; color: #4b5563; font-style: italic;\">\n \"Design is not just what it looks like and feels like. Design is how it works.\"\n </blockquote>\n </div>\n\n <!-- 2. SIDEBAR NAV — active state indicator -->\n <h2>Sidebar Navigation (Active State)</h2>\n <div class=\"demo\">\n <nav style=\"width: 200px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;\">\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\">Dashboard</a>\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #1d4ed8; text-decoration: none; border-left: 3px solid #3b82f6; background: #eff6ff; font-weight: 500;\">Projects</a>\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\">Settings</a>\n </nav>\n </div>\n\n <!-- 3. FORM VALIDATION — error indicator on input -->\n <h2>Form Validation</h2>\n <div class=\"demo\">\n <div style=\"margin-bottom: 1rem;\">\n <label style=\"display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem;\">Email</label>\n <input type=\"email\" value=\"not-an-email\" style=\"width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #fca5a5; border-left: 3px solid #b91c1c; border-radius: 6px; outline: none; font-size: 0.875rem;\">\n <p style=\"color: #b91c1c; font-size: 0.75rem; margin-top: 0.25rem;\">Please enter a valid email address.</p>\n </div>\n </div>\n\n <!-- 4. TIMELINE / STEPPER -->\n <h2>Timeline</h2>\n <div class=\"demo\">\n <div style=\"border-left: 3px solid #e5e7eb; padding-left: 1.5rem; margin-left: 0.75rem;\">\n <div style=\"position: relative; padding-bottom: 1.5rem;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0;\">Order placed</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">March 15, 2026</p>\n </div>\n <div style=\"position: relative; padding-bottom: 1.5rem;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0;\">Shipped</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">March 16, 2026</p>\n </div>\n <div style=\"position: relative;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #d1d5db; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0; color: #4b5563;\">Delivered</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">Expected March 18</p>\n </div>\n </div>\n </div>\n\n <!-- 5. CODE BLOCK — diff highlighting -->\n <h2>Code Diff Highlighting</h2>\n <div class=\"demo\">\n <pre style=\"background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-size: 0.8125rem; overflow-x: auto; margin: 0;\">\n<code><span style=\"display: block; border-left: 3px solid #ef4444; padding-left: 0.75rem; background: rgba(239,68,68,0.1);\">- const old = getValue();</span>\n<span style=\"display: block; border-left: 3px solid #22c55e; padding-left: 0.75rem; background: rgba(34,197,94,0.1);\">+ const value = getNewValue();</span>\n<span style=\"display: block; padding-left: calc(3px + 0.75rem);\"> return value;</span></code></pre>\n </div>\n\n <!-- 6. TAB ACTIVE STATE — bottom border -->\n <h2>Tab Navigation</h2>\n <div class=\"demo\">\n <div style=\"display: flex; border-bottom: 1px solid #e5e7eb;\">\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #1d4ed8; border-bottom: 2px solid #3b82f6; font-weight: 500; cursor: pointer;\">Overview</button>\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\">Analytics</button>\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\">Settings</button>\n </div>\n </div>\n\n <!-- 7. TABLE CELL BORDERS -->\n <h2>Data Table</h2>\n <div class=\"demo\">\n <table style=\"width: 100%; border-collapse: collapse; font-size: 0.875rem;\">\n <thead>\n <tr>\n <th style=\"text-align: left; padding: 0.5rem; border-bottom: 2px solid #111827;\">Name</th>\n <th style=\"text-align: right; padding: 0.5rem; border-bottom: 2px solid #111827;\">Revenue</th>\n </tr>\n </thead>\n <tbody>\n <tr><td style=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">Acme Corp</td><td style=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">$1.2M</td></tr>\n <tr><td style=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">Globex</td><td style=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">$850K</td></tr>\n </tbody>\n </table>\n </div>\n\n <!-- 8. ALERT BANNER — full-width, not a card -->\n <h2>Alert Banner</h2>\n <div class=\"demo\">\n <div style=\"border: 1px solid #f59e0b; background: #fffbeb; padding: 0.75rem 1rem; font-size: 0.875rem; color: #92400e;\">\n <strong>Warning:</strong> Your trial expires in 3 days. <a href=\"#\" style=\"color: #d97706;\">Upgrade now</a>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }\n .demo { max-width: 36rem; margin-bottom: 1.5rem; }\n \nstyle=\"color: #6b7280; margin-bottom: 2rem;\"\nstyle=\"border-left: 4px solid #d1d5db; padding-left: 1rem; margin: 0; color: #4b5563; font-style: italic;\"\nstyle=\"width: 200px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #1d4ed8; text-decoration: none; border-left: 3px solid #3b82f6; background: #eff6ff; font-weight: 500;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\"\nstyle=\"margin-bottom: 1rem;\"\nstyle=\"display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem;\"\nstyle=\"width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #fca5a5; border-left: 3px solid #b91c1c; border-radius: 6px; outline: none; font-size: 0.875rem;\"\nstyle=\"color: #b91c1c; font-size: 0.75rem; margin-top: 0.25rem;\"\nstyle=\"border-left: 3px solid #e5e7eb; padding-left: 1.5rem; margin-left: 0.75rem;\"\nstyle=\"position: relative; padding-bottom: 1.5rem;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"position: relative; padding-bottom: 1.5rem;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"position: relative;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #d1d5db; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0; color: #4b5563;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-size: 0.8125rem; overflow-x: auto; margin: 0;\"\nstyle=\"display: block; border-left: 3px solid #ef4444; padding-left: 0.75rem; background: rgba(239,68,68,0.1);\"\nstyle=\"display: block; border-left: 3px solid #22c55e; padding-left: 0.75rem; background: rgba(34,197,94,0.1);\"\nstyle=\"display: block; padding-left: calc(3px + 0.75rem);\"\nstyle=\"display: flex; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #1d4ed8; border-bottom: 2px solid #3b82f6; font-weight: 500; cursor: pointer;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\"\nstyle=\"width: 100%; border-collapse: collapse; font-size: 0.875rem;\"\nstyle=\"text-align: left; padding: 0.5rem; border-bottom: 2px solid #111827;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 2px solid #111827;\"\nstyle=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"border: 1px solid #f59e0b; background: #fffbeb; padding: 0.75rem 1rem; font-size: 0.875rem; color: #92400e;\"\nstyle=\"color: #d97706;\"","classText":"demo\ndemo\ndemo\ndemo\ndemo\ndemo\ndemo\ndemo"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Anti-Patterns From Linked Stylesheet</title>\n <link rel=\"stylesheet\" href=\"external-styles.css\">\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1080px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n }\n\n p.intro {\n max-width: 1080px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1080px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .cards {\n display: grid;\n gap: 14px;\n }\n\n h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .cards p {\n margin: 0;\n color: #4b5563;\n }\n </style>\n</head>\n<body>\n <h1>Linked Stylesheet Anti-Patterns</h1>\n <p class=\"intro\">\n These elements get their anti-pattern styles from an external CSS file.\n Regex-only scanning misses these — only computed style analysis catches them.\n </p>\n\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"cards\">\n <div class=\"external-side-tab\">\n <h3>External side-tab class</h3>\n <p>border-left plus border-radius from a linked stylesheet.</p>\n </div>\n\n <div class=\"external-top-accent\">\n <h3>External top accent class</h3>\n <p>border-top plus border-radius from a linked stylesheet.</p>\n </div>\n </div>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"cards\">\n <div class=\"external-clean\">\n <h3>External clean card</h3>\n <p>Uniform 1px border from the linked stylesheet.</p>\n </div>\n\n <div class=\"external-neutral-side\">\n <h3>External neutral side rule</h3>\n <p>Thick side border, but the color is structural gray.</p>\n </div>\n\n <div class=\"external-square-top\">\n <h3>External square top rule</h3>\n <p>Heavy top border with no rounded card corners.</p>\n </div>\n </div>\n </section>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1080px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n }\n\n p.intro {\n max-width: 1080px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1080px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .cards {\n display: grid;\n gap: 14px;\n }\n\n h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .cards p {\n margin: 0;\n color: #4b5563;\n }\n \n/* External stylesheet that applies anti-pattern styles */\n\n/* Side-tab via external class */\n.external-side-tab {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-left: 4px solid #3b82f6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Top border accent via external class */\n.external-top-accent {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-top: 3px solid #8b5cf6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Overused font from external stylesheet */\n.external-inter {\n font-family: 'Inter', sans-serif;\n}\n\n/* Clean card — should NOT flag */\n.external-clean {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Neutral side rule — should NOT flag */\n.external-neutral-side {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-left: 4px solid #d1d5db;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Top rule without rounded card corners — should NOT flag */\n.external-square-top {\n background: white;\n padding: 1rem;\n border-radius: 0;\n border-top: 4px solid #8b5cf6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n","classText":"intro\ngrid\ncol\ncards\nexternal-side-tab\nexternal-top-accent\ncol\ncards\nexternal-clean\nexternal-neutral-side\nexternal-square-top"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Side-Tab with Modern Color Formats (oklch/lab/lch/hsl/hwb)</title>\n <style>\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n The side-tab snippet doesn't embed identifying text, so the test\n asserts finding counts against the structural ids (#flag-N / #pass-N)\n by reading the element via data attributes in the check path. Since\n that isn't available to the Node fixture runner, we instead place a\n unique number of FLAG elements (each a distinct side-tab) and verify\n the side-tab count. */\n\n /* CSS custom properties — variables used by the var()-case fixtures.\n jsdom's CSSOM drops any border shorthand containing var(), which used\n to make every side-tab built with CSS variables invisible to the\n detector. These cases regression-test the fallback pass that parses\n border shorthands directly off the rule (not the computed style). */\n :root {\n --brand: #87a8ff;\n --warn: #8a6a2f;\n --terracotta: #aa674d;\n --line: #e5e7eb; /* neutral gray — must NOT trigger */\n --rule: #f3f4f6; /* near-white — must NOT trigger */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using modern color formats ── */\n\n /* 1: oklch border-left + border-radius (the canonical real-page bug) */\n #flag-oklch-1 {\n width: 400px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n }\n\n /* 2: oklch mixed — neutral 1px border all around + colored 4px left +\n border-radius (mirrors .medication-card from the reproducer) */\n #flag-oklch-2 {\n width: 400px;\n background: oklch(100% 0 0);\n border: 1px solid oklch(80% 0.05 250);\n border-left: 4px solid oklch(75% 0.15 45);\n border-radius: 4px;\n }\n\n /* 3: oklch alert pattern — colored 4px border-left + border-radius */\n #flag-oklch-3 {\n width: 400px;\n background: oklch(98% 0.01 60);\n border-left: 4px solid oklch(75% 0.18 45);\n border-radius: 4px;\n }\n\n /* 4: oklab border-left + border-radius */\n #flag-oklab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid oklab(60% 0.1 0.15);\n }\n\n /* 5: lch border-left + border-radius */\n #flag-lch-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lch(55% 60 250);\n }\n\n /* 6: lab border-left + border-radius */\n #flag-lab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lab(50% 40 -30);\n }\n\n /* 7: card-shaped <label> with colored oklch side border + radius\n (the checklist-item pattern from preop-portal reproducer) */\n label#flag-label-oklch {\n display: grid;\n grid-template-columns: auto 1fr;\n width: 400px;\n padding: 16px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n cursor: pointer;\n }\n\n /* 8: card-shaped <label> with colored rgb side border + radius —\n proves the fix is not oklch-specific */\n label#flag-label-rgb {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid #3b82f6;\n cursor: pointer;\n }\n\n /* 9: CSS variable in shorthand — the canonical real-page pattern from\n evals (e.g. 09-physics-study: border-left:8px solid var(--brand-2)).\n jsdom silently drops this entire declaration, so the computed-style\n path used to miss it. */\n #flag-var-shorthand {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid var(--brand);\n }\n\n /* 10: CSS variable + neutral 1px all-around border — mirrors the\n medication-card pattern with a colored accent on the left. */\n #flag-var-mixed {\n width: 400px;\n background: #ffffff;\n border: 1px solid var(--line);\n border-left: 4px solid var(--warn);\n border-radius: 4px;\n }\n\n /* 11: CSS variable shorthand, right side. Ensures the fallback works\n for both inline axes, not just border-left. */\n #flag-var-right {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 4px solid var(--terracotta);\n }\n\n /* 12: card-shaped <label> with var() side border + radius —\n the checklist-row shape, built with tokens. */\n label#flag-label-var {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid var(--brand);\n cursor: pointer;\n }\n\n /* ── PASS cases: legitimate or neutral borders, must NOT fire ── */\n\n /* 1: oklch chroma ~0 — true neutral gray border should not fire */\n #pass-oklch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(80% 0 0);\n }\n\n /* 2: oklch low chroma (0.01) — still perceptually neutral */\n #pass-oklch-nearneutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(75% 0.01 250);\n }\n\n /* 3: oklch colored border all around (not one-sided) — no side-tab */\n #pass-oklch-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid oklch(65% 0.12 250);\n border-radius: 4px;\n }\n\n /* 4: oklch colored 1px border-left (too thin to qualify) */\n #pass-oklch-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid oklch(65% 0.12 250);\n }\n\n /* 5: lab neutral (chroma near zero) — must not fire */\n #pass-lab-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lab(50% 0 0);\n }\n\n /* 6: lch neutral (chroma 0) */\n #pass-lch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lch(50% 0 0);\n }\n\n /* 7: plain inline form label — no border, no padding, no radius.\n Must NOT be flagged for anything. */\n label#pass-label-plain {\n font-size: 14px;\n font-weight: 500;\n color: #374151;\n }\n\n /* 8: form label with a small neutral gray left border — neutral color\n should not fire the colored side-tab rule. */\n label#pass-label-neutral-border {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid #e5e7eb;\n }\n\n /* 9: inline label inside a form row — default inline layout,\n no borders, must not fire. */\n label#pass-label-inline {\n font-size: 13px;\n color: #6b7280;\n margin-right: 8px;\n }\n\n /* 10: label with 1px colored left border — too thin to qualify\n (matches the existing 1px thin rule for divs). */\n label#pass-label-thin {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid #3b82f6;\n }\n\n /* 11: CSS variable resolving to a neutral gray — must NOT fire,\n even though the border shorthand contains var(). */\n #pass-var-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid var(--line);\n }\n\n /* 12: thin (1px) var-based left border — under the side-tab width\n threshold even when resolved. */\n #pass-var-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid var(--brand);\n }\n\n /* 13: var-based uniform border on all four sides. Not a side-tab. */\n #pass-var-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid var(--brand);\n border-radius: 4px;\n }\n\n /* 14: neutral 1px oklch right divider — context-bar hairline, not a side-tab. */\n #pass-oklch-neutral-right-divider {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 1px solid oklch(92% 0 0 / 0.12);\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"case\" id=\"flag-oklch-1\"><h3>oklch checklist</h3><p>oklch border-left 3px + radius</p></div>\n <div class=\"case\" id=\"flag-oklch-2\"><h3>oklch medication card</h3><p>neutral all + colored left 4px</p></div>\n <div class=\"case\" id=\"flag-oklch-3\"><h3>oklch alert</h3><p>warm border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-oklab-1\"><h3>oklab card</h3><p>oklab border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-lch-1\"><h3>lch card</h3><p>lch border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-lab-1\"><h3>lab card</h3><p>lab border-left 4px + radius</p></div>\n <label class=\"case\" id=\"flag-label-oklch\">\n <input type=\"checkbox\">\n <span>Card-shaped label with oklch side border</span>\n </label>\n <label class=\"case\" id=\"flag-label-rgb\">\n <input type=\"checkbox\">\n <span>Card-shaped label with rgb side border</span>\n </label>\n <div class=\"case\" id=\"flag-var-shorthand\"><h3>var() shorthand</h3><p>border-left 5px + var(--brand)</p></div>\n <div class=\"case\" id=\"flag-var-mixed\"><h3>var() mixed</h3><p>neutral all + colored var left</p></div>\n <div class=\"case\" id=\"flag-var-right\"><h3>var() right side</h3><p>border-right 4px + var(--terracotta)</p></div>\n <label class=\"case\" id=\"flag-label-var\">\n <input type=\"checkbox\">\n <span>Card-shaped label with var() side border</span>\n </label>\n </div>\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"case\" id=\"pass-oklch-neutral\"><h3>oklch neutral</h3><p>chroma 0 gray</p></div>\n <div class=\"case\" id=\"pass-oklch-nearneutral\"><h3>oklch near-neutral</h3><p>chroma 0.01</p></div>\n <div class=\"case\" id=\"pass-oklch-allsides\"><h3>oklch all sides</h3><p>uniform border</p></div>\n <div class=\"case\" id=\"pass-oklch-thin\"><h3>oklch thin</h3><p>1px too thin</p></div>\n <div class=\"case\" id=\"pass-lab-neutral\"><h3>lab neutral</h3><p>chroma 0</p></div>\n <div class=\"case\" id=\"pass-lch-neutral\"><h3>lch neutral</h3><p>chroma 0</p></div>\n <div>\n <label for=\"pass-email\" id=\"pass-label-plain\">Email address</label>\n <input type=\"email\" id=\"pass-email\">\n </div>\n <label id=\"pass-label-neutral-border\">\n <input type=\"checkbox\">\n <span>Label with neutral gray left border</span>\n </label>\n <form>\n <label for=\"pass-name\" id=\"pass-label-inline\">Name:</label>\n <input type=\"text\" id=\"pass-name\">\n </form>\n <label id=\"pass-label-thin\">\n <input type=\"checkbox\">\n <span>Label with 1px colored left border (too thin)</span>\n </label>\n <div class=\"case\" id=\"pass-var-neutral\"><h3>var() neutral</h3><p>--line resolves to gray</p></div>\n <div class=\"case\" id=\"pass-var-thin\"><h3>var() thin</h3><p>1px too thin to qualify</p></div>\n <div class=\"case\" id=\"pass-var-allsides\"><h3>var() all sides</h3><p>uniform, not a side-tab</p></div>\n <div class=\"case\" id=\"pass-oklch-neutral-right-divider\"><h3>oklch neutral right divider</h3><p>1px neutral context-bar hairline</p></div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n The side-tab snippet doesn't embed identifying text, so the test\n asserts finding counts against the structural ids (#flag-N / #pass-N)\n by reading the element via data attributes in the check path. Since\n that isn't available to the Node fixture runner, we instead place a\n unique number of FLAG elements (each a distinct side-tab) and verify\n the side-tab count. */\n\n /* CSS custom properties — variables used by the var()-case fixtures.\n jsdom's CSSOM drops any border shorthand containing var(), which used\n to make every side-tab built with CSS variables invisible to the\n detector. These cases regression-test the fallback pass that parses\n border shorthands directly off the rule (not the computed style). */\n :root {\n --brand: #87a8ff;\n --warn: #8a6a2f;\n --terracotta: #aa674d;\n --line: #e5e7eb; /* neutral gray — must NOT trigger */\n --rule: #f3f4f6; /* near-white — must NOT trigger */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using modern color formats ── */\n\n /* 1: oklch border-left + border-radius (the canonical real-page bug) */\n #flag-oklch-1 {\n width: 400px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n }\n\n /* 2: oklch mixed — neutral 1px border all around + colored 4px left +\n border-radius (mirrors .medication-card from the reproducer) */\n #flag-oklch-2 {\n width: 400px;\n background: oklch(100% 0 0);\n border: 1px solid oklch(80% 0.05 250);\n border-left: 4px solid oklch(75% 0.15 45);\n border-radius: 4px;\n }\n\n /* 3: oklch alert pattern — colored 4px border-left + border-radius */\n #flag-oklch-3 {\n width: 400px;\n background: oklch(98% 0.01 60);\n border-left: 4px solid oklch(75% 0.18 45);\n border-radius: 4px;\n }\n\n /* 4: oklab border-left + border-radius */\n #flag-oklab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid oklab(60% 0.1 0.15);\n }\n\n /* 5: lch border-left + border-radius */\n #flag-lch-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lch(55% 60 250);\n }\n\n /* 6: lab border-left + border-radius */\n #flag-lab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lab(50% 40 -30);\n }\n\n /* 7: card-shaped <label> with colored oklch side border + radius\n (the checklist-item pattern from preop-portal reproducer) */\n label#flag-label-oklch {\n display: grid;\n grid-template-columns: auto 1fr;\n width: 400px;\n padding: 16px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n cursor: pointer;\n }\n\n /* 8: card-shaped <label> with colored rgb side border + radius —\n proves the fix is not oklch-specific */\n label#flag-label-rgb {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid #3b82f6;\n cursor: pointer;\n }\n\n /* 9: CSS variable in shorthand — the canonical real-page pattern from\n evals (e.g. 09-physics-study: border-left:8px solid var(--brand-2)).\n jsdom silently drops this entire declaration, so the computed-style\n path used to miss it. */\n #flag-var-shorthand {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid var(--brand);\n }\n\n /* 10: CSS variable + neutral 1px all-around border — mirrors the\n medication-card pattern with a colored accent on the left. */\n #flag-var-mixed {\n width: 400px;\n background: #ffffff;\n border: 1px solid var(--line);\n border-left: 4px solid var(--warn);\n border-radius: 4px;\n }\n\n /* 11: CSS variable shorthand, right side. Ensures the fallback works\n for both inline axes, not just border-left. */\n #flag-var-right {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 4px solid var(--terracotta);\n }\n\n /* 12: card-shaped <label> with var() side border + radius —\n the checklist-row shape, built with tokens. */\n label#flag-label-var {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid var(--brand);\n cursor: pointer;\n }\n\n /* ── PASS cases: legitimate or neutral borders, must NOT fire ── */\n\n /* 1: oklch chroma ~0 — true neutral gray border should not fire */\n #pass-oklch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(80% 0 0);\n }\n\n /* 2: oklch low chroma (0.01) — still perceptually neutral */\n #pass-oklch-nearneutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(75% 0.01 250);\n }\n\n /* 3: oklch colored border all around (not one-sided) — no side-tab */\n #pass-oklch-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid oklch(65% 0.12 250);\n border-radius: 4px;\n }\n\n /* 4: oklch colored 1px border-left (too thin to qualify) */\n #pass-oklch-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid oklch(65% 0.12 250);\n }\n\n /* 5: lab neutral (chroma near zero) — must not fire */\n #pass-lab-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lab(50% 0 0);\n }\n\n /* 6: lch neutral (chroma 0) */\n #pass-lch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lch(50% 0 0);\n }\n\n /* 7: plain inline form label — no border, no padding, no radius.\n Must NOT be flagged for anything. */\n label#pass-label-plain {\n font-size: 14px;\n font-weight: 500;\n color: #374151;\n }\n\n /* 8: form label with a small neutral gray left border — neutral color\n should not fire the colored side-tab rule. */\n label#pass-label-neutral-border {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid #e5e7eb;\n }\n\n /* 9: inline label inside a form row — default inline layout,\n no borders, must not fire. */\n label#pass-label-inline {\n font-size: 13px;\n color: #6b7280;\n margin-right: 8px;\n }\n\n /* 10: label with 1px colored left border — too thin to qualify\n (matches the existing 1px thin rule for divs). */\n label#pass-label-thin {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid #3b82f6;\n }\n\n /* 11: CSS variable resolving to a neutral gray — must NOT fire,\n even though the border shorthand contains var(). */\n #pass-var-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid var(--line);\n }\n\n /* 12: thin (1px) var-based left border — under the side-tab width\n threshold even when resolved. */\n #pass-var-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid var(--brand);\n }\n\n /* 13: var-based uniform border on all four sides. Not a side-tab. */\n #pass-var-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid var(--brand);\n border-radius: 4px;\n }\n\n /* 14: neutral 1px oklch right divider — context-bar hairline, not a side-tab. */\n #pass-oklch-neutral-right-divider {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 1px solid oklch(92% 0 0 / 0.12);\n }\n ","classText":"grid\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Motion Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n /* ─── Two-column fixture convention ─────────────────────────────\n Left column = should flag. Right column = should pass.\n Tests assert (a) every flag-column rule fires and (b) the total\n finding count matches the number of flag cases (no false\n positives leaking from the pass column).\n ──────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .demo { padding: 14px 16px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 10px; }\n .demo h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .demo p { font-size: 12px; color: #6b7280; margin: 4px 0 0; }\n\n /* ── FLAG: bounce / elastic easing ── */\n @keyframes bounce-keyframe {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n .bounce-animation { animation: bounce-keyframe 1s infinite; }\n .elastic-transition { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }\n\n /* ── FLAG: layout property transitions ── */\n .width-transition { transition: width 0.3s ease; }\n .height-transition { transition: height 0.4s ease-out; }\n .padding-transition { transition: padding 0.2s linear; }\n .margin-transition { transition: margin 0.3s ease-in; }\n .max-height-transition { transition: max-height 0.5s ease; }\n .multi-layout-transition { transition: width 0.3s ease, height 0.3s ease; }\n .mixed-transition { transition: width 0.3s ease, opacity 0.3s ease; }\n .transition-property-width { transition-property: width; transition-duration: 0.3s; }\n\n /* ── PASS: good easing ── */\n @keyframes fade-in-keyframe {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .fade-in-good { animation: fade-in-keyframe 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n .ease-out-quart { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }\n .ease-out-expo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }\n\n /* ── PASS: safe transitions ── */\n .transform-transition { transition: transform 0.3s ease-out; }\n .opacity-transition { transition: opacity 0.2s ease; }\n .color-transition { transition: color 0.15s ease, background-color 0.15s ease; }\n .shadow-transition { transition: box-shadow 0.2s ease; }\n .all-transition { transition: all 0.3s ease; }\n .multi-safe-transition { transition: transform 0.3s ease, opacity 0.3s ease, box-shadow 0.2s ease; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Bounce / elastic easing</h3>\n <div class=\"demo bounce-animation\">\n <h4>CSS bounce animation</h4>\n <p>animation: bounce 1s infinite</p>\n </div>\n <div class=\"demo elastic-transition\">\n <h4>Elastic cubic-bezier</h4>\n <p>cubic-bezier(0.68, -0.55, 0.265, 1.55)</p>\n </div>\n\n <h3>Layout property transitions</h3>\n <div class=\"demo width-transition\">\n <h4>transition: width</h4>\n <p>Animating width causes layout thrash.</p>\n </div>\n <div class=\"demo height-transition\">\n <h4>transition: height</h4>\n <p>Animating height causes layout thrash.</p>\n </div>\n <div class=\"demo padding-transition\">\n <h4>transition: padding</h4>\n <p>Animating padding causes layout thrash.</p>\n </div>\n <div class=\"demo margin-transition\">\n <h4>transition: margin</h4>\n <p>Animating margin causes layout thrash.</p>\n </div>\n <div class=\"demo max-height-transition\">\n <h4>transition: max-height</h4>\n <p>Use grid-template-rows instead.</p>\n </div>\n <div class=\"demo multi-layout-transition\">\n <h4>transition: width, height</h4>\n <p>Multiple layout properties.</p>\n </div>\n <div class=\"demo mixed-transition\">\n <h4>transition: width, opacity</h4>\n <p>Layout property mixed with OK property.</p>\n </div>\n <div class=\"demo transition-property-width\">\n <h4>transition-property: width</h4>\n <p>Longhand form.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Good easing</h3>\n <div class=\"demo fade-in-good\">\n <h4>Smooth fade in</h4>\n <p>animation: fadeIn with exponential ease-out</p>\n </div>\n <div class=\"demo ease-out-quart\">\n <h4>Ease-out quart</h4>\n <p>cubic-bezier(0.25, 1, 0.5, 1) — smooth deceleration</p>\n </div>\n <div class=\"demo ease-out-expo\">\n <h4>Ease-out expo</h4>\n <p>cubic-bezier(0.16, 1, 0.3, 1) — natural feel</p>\n </div>\n\n <h3>Safe transitions (transform / opacity / color only)</h3>\n <div class=\"demo transform-transition\">\n <h4>transition: transform</h4>\n <p>Transform is GPU-accelerated and safe.</p>\n </div>\n <div class=\"demo opacity-transition\">\n <h4>transition: opacity</h4>\n <p>Opacity is GPU-accelerated and safe.</p>\n </div>\n <div class=\"demo color-transition\">\n <h4>transition: color, background-color</h4>\n <p>Color transitions are paint-only, no layout.</p>\n </div>\n <div class=\"demo shadow-transition\">\n <h4>transition: box-shadow</h4>\n <p>Shadow transitions are paint-only.</p>\n </div>\n <div class=\"demo all-transition\">\n <h4>transition: all</h4>\n <p>Too common to flag — usually paired with transform/opacity.</p>\n </div>\n <div class=\"demo multi-safe-transition\">\n <h4>transition: transform, opacity, box-shadow</h4>\n <p>Multiple safe properties combined.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* ─── Two-column fixture convention ─────────────────────────────\n Left column = should flag. Right column = should pass.\n Tests assert (a) every flag-column rule fires and (b) the total\n finding count matches the number of flag cases (no false\n positives leaking from the pass column).\n ──────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .demo { padding: 14px 16px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 10px; }\n .demo h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .demo p { font-size: 12px; color: #6b7280; margin: 4px 0 0; }\n\n /* ── FLAG: bounce / elastic easing ── */\n @keyframes bounce-keyframe {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n .bounce-animation { animation: bounce-keyframe 1s infinite; }\n .elastic-transition { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }\n\n /* ── FLAG: layout property transitions ── */\n .width-transition { transition: width 0.3s ease; }\n .height-transition { transition: height 0.4s ease-out; }\n .padding-transition { transition: padding 0.2s linear; }\n .margin-transition { transition: margin 0.3s ease-in; }\n .max-height-transition { transition: max-height 0.5s ease; }\n .multi-layout-transition { transition: width 0.3s ease, height 0.3s ease; }\n .mixed-transition { transition: width 0.3s ease, opacity 0.3s ease; }\n .transition-property-width { transition-property: width; transition-duration: 0.3s; }\n\n /* ── PASS: good easing ── */\n @keyframes fade-in-keyframe {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .fade-in-good { animation: fade-in-keyframe 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n .ease-out-quart { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }\n .ease-out-expo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }\n\n /* ── PASS: safe transitions ── */\n .transform-transition { transition: transform 0.3s ease-out; }\n .opacity-transition { transition: opacity 0.2s ease; }\n .color-transition { transition: color 0.15s ease, background-color 0.15s ease; }\n .shadow-transition { transition: box-shadow 0.2s ease; }\n .all-transition { transition: all 0.3s ease; }\n .multi-safe-transition { transition: transform 0.3s ease, opacity 0.3s ease, box-shadow 0.2s ease; }\n ","classText":"grid\ncol\ndemo bounce-animation\ndemo elastic-transition\ndemo width-transition\ndemo height-transition\ndemo padding-transition\ndemo margin-transition\ndemo max-height-transition\ndemo multi-layout-transition\ndemo mixed-transition\ndemo transition-property-width\ncol\ndemo fade-in-good\ndemo ease-out-quart\ndemo ease-out-expo\ndemo transform-transition\ndemo opacity-transition\ndemo color-transition\ndemo shadow-transition\ndemo all-transition\ndemo multi-safe-transition"}],"result":[{"id":"bounce-easing","snippet":"animation: bounce-keyframe","selector":".bounce-animation"},{"id":"bounce-easing","snippet":"cubic-bezier(0.68, -0.55, 0.265, 1.55)","selector":".elastic-transition"},{"id":"layout-transition","snippet":"transition: width"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Side-Tab with CSS Named Colors (purple/rebeccapurple/crimson/teal)</title>\n <style>\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n Regression for issue #359: `border-left: 4px solid purple` in a .html\n file never fired side-tab because the static cascade's shorthand color\n extraction only recognized 9 named colors — every other spec name\n (purple, rebeccapurple, crimson, teal, ...) was dropped from the\n shorthand, leaving the side at the default black, which reads as\n neutral and silently skips the check. The same declaration in a .css\n file was flagged by the regex engine, so the two engines disagreed. */\n\n :root {\n --accent: orange; /* named color behind a var() — must resolve and flag */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using CSS named colors ── */\n\n /* 1: the exact reproducer from issue #359 — named purple in a <style>\n rule with background + padding + radius */\n #flag-named-purple {\n width: 400px;\n background: #f6f6f6;\n padding: 16px;\n border-left: 4px solid purple;\n border-radius: 8px;\n }\n\n /* 2: rebeccapurple — a longer name that contains another color name\n (\"purple\") as a substring; must match whole-token. Width 5px so the\n finding snippet is unique to this case. */\n #flag-named-rebecca {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid rebeccapurple;\n }\n\n /* 3: horizontal stripe variant — named crimson riding the top edge */\n #flag-named-crimson-top {\n width: 400px;\n background: #ffffff;\n border-top: 4px solid crimson;\n }\n\n /* 4: named teal, 3px, no radius — the bare w >= 3 arm */\n #flag-named-teal {\n width: 400px;\n background: #ffffff;\n border-left: 3px solid teal;\n }\n\n /* 5: named color behind a var() in the shorthand. Width 6px so the\n finding snippet is unique to this case. */\n #flag-named-var {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 6px solid var(--accent);\n }\n\n /* ── PASS cases: neutral named colors and non-side-tab shapes ── */\n\n /* 1: dimgray — chromatic-looking name, neutral value; must NOT fire */\n #pass-named-dimgray {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid dimgray;\n }\n\n /* 2: gainsboro — light neutral named color */\n #pass-named-gainsboro {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid gainsboro;\n }\n\n /* 3: named black side border — neutral, not a colored stripe */\n #pass-named-black {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid black;\n }\n\n /* 4: 1px named purple — too thin to qualify */\n #pass-named-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid purple;\n }\n\n /* 5: uniform named purple border on all four sides — not a side-tab */\n #pass-named-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid purple;\n border-radius: 4px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"case\" id=\"flag-named-purple\"><h3>named purple</h3><p>border-left 4px solid purple + radius</p></div>\n <div class=\"case\" id=\"flag-named-rebecca\"><h3>rebeccapurple</h3><p>border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-named-crimson-top\"><h3>crimson top stripe</h3><p>border-top 4px, horizontal variant</p></div>\n <div class=\"case\" id=\"flag-named-teal\"><h3>named teal</h3><p>border-left 3px, no radius</p></div>\n <div class=\"case\" id=\"flag-named-var\"><h3>var() to named</h3><p>border-left 4px solid var(--accent)</p></div>\n <div class=\"case\" style=\"width: 400px; background: #ffffff; border-left: 7px solid purple\">\n <h3>inline named purple</h3><p>style attribute, issue #359 case (a); width 7px keeps the snippet unique</p>\n </div>\n </div>\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"case\" id=\"pass-named-dimgray\"><h3>dimgray</h3><p>neutral named color</p></div>\n <div class=\"case\" id=\"pass-named-gainsboro\"><h3>gainsboro</h3><p>light neutral named color</p></div>\n <div class=\"case\" id=\"pass-named-black\"><h3>named black</h3><p>neutral side border</p></div>\n <div class=\"case\" id=\"pass-named-thin\"><h3>1px purple</h3><p>too thin to qualify</p></div>\n <div class=\"case\" id=\"pass-named-allsides\"><h3>uniform purple</h3><p>all four sides, not a stripe</p></div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n Regression for issue #359: `border-left: 4px solid purple` in a .html\n file never fired side-tab because the static cascade's shorthand color\n extraction only recognized 9 named colors — every other spec name\n (purple, rebeccapurple, crimson, teal, ...) was dropped from the\n shorthand, leaving the side at the default black, which reads as\n neutral and silently skips the check. The same declaration in a .css\n file was flagged by the regex engine, so the two engines disagreed. */\n\n :root {\n --accent: orange; /* named color behind a var() — must resolve and flag */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using CSS named colors ── */\n\n /* 1: the exact reproducer from issue #359 — named purple in a <style>\n rule with background + padding + radius */\n #flag-named-purple {\n width: 400px;\n background: #f6f6f6;\n padding: 16px;\n border-left: 4px solid purple;\n border-radius: 8px;\n }\n\n /* 2: rebeccapurple — a longer name that contains another color name\n (\"purple\") as a substring; must match whole-token. Width 5px so the\n finding snippet is unique to this case. */\n #flag-named-rebecca {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid rebeccapurple;\n }\n\n /* 3: horizontal stripe variant — named crimson riding the top edge */\n #flag-named-crimson-top {\n width: 400px;\n background: #ffffff;\n border-top: 4px solid crimson;\n }\n\n /* 4: named teal, 3px, no radius — the bare w >= 3 arm */\n #flag-named-teal {\n width: 400px;\n background: #ffffff;\n border-left: 3px solid teal;\n }\n\n /* 5: named color behind a var() in the shorthand. Width 6px so the\n finding snippet is unique to this case. */\n #flag-named-var {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 6px solid var(--accent);\n }\n\n /* ── PASS cases: neutral named colors and non-side-tab shapes ── */\n\n /* 1: dimgray — chromatic-looking name, neutral value; must NOT fire */\n #pass-named-dimgray {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid dimgray;\n }\n\n /* 2: gainsboro — light neutral named color */\n #pass-named-gainsboro {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid gainsboro;\n }\n\n /* 3: named black side border — neutral, not a colored stripe */\n #pass-named-black {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid black;\n }\n\n /* 4: 1px named purple — too thin to qualify */\n #pass-named-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid purple;\n }\n\n /* 5: uniform named purple border on all four sides — not a side-tab */\n #pass-named-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid purple;\n border-radius: 4px;\n }\n \nstyle=\"width: 400px; background: #ffffff; border-left: 7px solid purple\"","classText":"grid\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncol\ncase\ncase\ncase\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Nav CTA contrast construction sweep</title>\n<style>\n :root{\n --v3bg: #f0b64e;\n --v3fg: #cfd9e9;\n }\n body{ margin:0; font-family: Georgia, serif; color:#1c2430; background:#ffffff; }\n header{ display:flex; align-items:center; gap:2rem; padding:1rem 2rem; background:#12213a; }\n .nav{ display:flex; gap:1.5rem; align-items:center; }\n .nav a{ font-size:0.9rem; text-decoration:none; }\n .cta{ display:inline-block; padding:0.6em 1.1em; border-radius:6px; font-weight:600; }\n\n /* v1: solid bg + own low-contrast color, both on the button class */\n a.cta.v1{ background:#e8b84b; color:#c9d6ea; }\n\n /* v2: specificity cascade — .nav a (0,1,1) beats .v2 (0,1,0) for color */\n .nav a{ color:#b7c6de; }\n .v2{ background:#e5a93f; color:#26313f; }\n\n /* v3: var() indirection for both colors */\n a.cta.v3{ background:var(--v3bg); color:var(--v3fg); }\n\n /* v4: gradient background, low-contrast text against every stop */\n a.cta.v4{ background:linear-gradient(90deg,#f2b854,#e8a83e); color:#d3dcec; }\n\n /* v5: pseudo-element paints the surface; button itself transparent */\n a.cta.v5{ position:relative; background:transparent; color:#d0daea; }\n .v5::before{ content:\"\"; position:absolute; inset:0; border-radius:6px; background:#efb352; z-index:-1; }\n\n /* v6: color inherited from header, button only sets bg */\n header{ color:#ccd8ea; }\n .v6{ background:#eab04a; }\n\n /* v7: alpha-composited own bg over the dark header */\n a.cta.v7{ background:rgb(240 178 74 / 0.85); color:#d2dcec; }\n\n /* v8: oklch serialization for both sides */\n a.cta.v8{ background:oklch(0.82 0.15 75); color:oklch(0.80 0.05 258); }\n\n /* v9: background-image url only (no computable color) + low contrast vs header */\n a.cta.v9{ background-image:url(\"data:image/gif;base64,R0lGODlhAQABAIAAAP7uSwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==\"); color:#d5deed; }\n</style>\n</head>\n<body>\n<header>\n <span style=\"color:#f4f7fb;font-weight:700\">Acme</span>\n <nav class=\"nav\" aria-label=\"Primary\">\n <a href=\"#a\">Product</a>\n <a href=\"#b\">Docs</a>\n <a class=\"cta v1\" href=\"#s\">Start now v1</a>\n <a class=\"cta v2\" href=\"#s\">Start now v2</a>\n <a class=\"cta v3\" href=\"#s\">Start now v3</a>\n <a class=\"cta v4\" href=\"#s\">Start now v4</a>\n <a class=\"cta v5\" href=\"#s\">Start now v5</a>\n <a class=\"cta v6\" href=\"#s\">Start now v6</a>\n <a class=\"cta v7\" href=\"#s\">Start now v7</a>\n <a class=\"cta v8\" href=\"#s\">Start now v8</a>\n <a class=\"cta v9\" href=\"#s\">Start now v9</a>\n </nav>\n</header>\n<main style=\"padding:4rem 2rem; max-width:60ch;\">\n <h1 style=\"font-size:2.4rem; margin:0 0 1rem;\">A page that exists to host a header</h1>\n <p style=\"line-height:1.6;\">The paragraphs below the header keep the document honest so the\n detector treats this as a full page rather than a fragment. Nothing here should trip any rule.</p>\n <p style=\"line-height:1.6;\">Second paragraph of ordinary body copy with comfortable measure and\n line height, dark ink on paper.</p>\n</main>\n</body>\n</html>\n",{"styleText":"\n :root{\n --v3bg: #f0b64e;\n --v3fg: #cfd9e9;\n }\n body{ margin:0; font-family: Georgia, serif; color:#1c2430; background:#ffffff; }\n header{ display:flex; align-items:center; gap:2rem; padding:1rem 2rem; background:#12213a; }\n .nav{ display:flex; gap:1.5rem; align-items:center; }\n .nav a{ font-size:0.9rem; text-decoration:none; }\n .cta{ display:inline-block; padding:0.6em 1.1em; border-radius:6px; font-weight:600; }\n\n /* v1: solid bg + own low-contrast color, both on the button class */\n a.cta.v1{ background:#e8b84b; color:#c9d6ea; }\n\n /* v2: specificity cascade — .nav a (0,1,1) beats .v2 (0,1,0) for color */\n .nav a{ color:#b7c6de; }\n .v2{ background:#e5a93f; color:#26313f; }\n\n /* v3: var() indirection for both colors */\n a.cta.v3{ background:var(--v3bg); color:var(--v3fg); }\n\n /* v4: gradient background, low-contrast text against every stop */\n a.cta.v4{ background:linear-gradient(90deg,#f2b854,#e8a83e); color:#d3dcec; }\n\n /* v5: pseudo-element paints the surface; button itself transparent */\n a.cta.v5{ position:relative; background:transparent; color:#d0daea; }\n .v5::before{ content:\"\"; position:absolute; inset:0; border-radius:6px; background:#efb352; z-index:-1; }\n\n /* v6: color inherited from header, button only sets bg */\n header{ color:#ccd8ea; }\n .v6{ background:#eab04a; }\n\n /* v7: alpha-composited own bg over the dark header */\n a.cta.v7{ background:rgb(240 178 74 / 0.85); color:#d2dcec; }\n\n /* v8: oklch serialization for both sides */\n a.cta.v8{ background:oklch(0.82 0.15 75); color:oklch(0.80 0.05 258); }\n\n /* v9: background-image url only (no computable color) + low contrast vs header */\n a.cta.v9{ background-image:url(\"data:image/gif;base64,R0lGODlhAQABAIAAAP7uSwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==\"); color:#d5deed; }\n\nstyle=\"color:#f4f7fb;font-weight:700\"\nstyle=\"padding:4rem 2rem; max-width:60ch;\"\nstyle=\"font-size:2.4rem; margin:0 0 1rem;\"\nstyle=\"line-height:1.6;\"\nstyle=\"line-height:1.6;\"","classText":"nav\ncta v1\ncta v2\ncta v3\ncta v4\ncta v5\ncta v6\ncta v7\ncta v8\ncta v9"}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Regression fixture for issue #408: tiny-text / undersized-ui-text must only\n measure rendered text. On sites that set `html { font-size: 62.5% }` the root\n computes to 10px, so every element that inherits the root and carries text\n content — including <script>, <style>, <title>, <noscript>, and display:none /\n visibility:hidden blocks — reports a 10px computed size. Their JS / CSS /\n JSON-LD text satisfies hasDirectText, so before the fix they produced dozens\n of phantom \"10px body text\" findings on every Shopify page.\n\n Explicit pixel sizes throughout because jsdom does no layout.\n-->\n<html style=\"font-size: 10px\">\n<head>\n<meta charset=\"utf-8\">\n<!-- SHOULD PASS: a long <title> inherits the 10px root, but nothing renders it. -->\n<title>This is a fairly long document title that easily exceeds twenty characters</title>\n<!-- SHOULD PASS: stylesheet text is never painted. -->\n<style>\n /* This CSS comment is deliberately longer than twenty characters so the style\n element's text node clears the hasDirectText / textLen > 20 gate. */\n body { font-size: 10px; font-family: system-ui, sans-serif; }\n .rendered-body { font-size: 10px; }\n .rendered-nav-link { font-size: 9px; }\n .none-block { display: none; font-size: 10px; }\n .hidden-block { visibility: hidden; font-size: 10px; }\n</style>\n<!-- SHOULD PASS: script payload text is never painted. -->\n<script>window.__ANALYTICS__ = { id: 1, ts: 0 }; console.log(\"an analytics payload string that is clearly longer than twenty characters\");</script>\n<!-- SHOULD PASS: JSON-LD schema block, a Shopify staple. -->\n<script type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"Product\",\"name\":\"A product name long enough to exceed twenty characters\"}</script>\n<!-- SHOULD PASS: noscript fallback prose is only shown without JS; still non-rendered here. -->\n<noscript>Please enable JavaScript in your browser to view this page content correctly.</noscript>\n</head>\n<body>\n\n<!-- SHOULD PASS: display:none block, its text is never painted. -->\n<div class=\"none-block\">Hidden display-none block of body text long enough to exceed twenty characters.</div>\n\n<!-- SHOULD PASS: visibility:hidden paragraph, its text is never painted. -->\n<p class=\"hidden-block\">Invisible visibility-hidden paragraph copy that is longer than the twenty char gate.</p>\n\n<!-- SHOULD FLAG (tiny-text): genuinely rendered body copy at 10px. -->\n<p class=\"rendered-body\">This is real rendered body copy at 10px that is definitely long enough to flag.</p>\n\n<!-- SHOULD FLAG (undersized-ui-text): genuinely rendered interactive text at 9px. -->\n<nav aria-label=\"primary\"><a href=\"/docs\" class=\"rendered-nav-link\">Rendered Nav Link</a></nav>\n\n</body>\n</html>\n",{"styleText":"\n /* This CSS comment is deliberately longer than twenty characters so the style\n element's text node clears the hasDirectText / textLen > 20 gate. */\n body { font-size: 10px; font-family: system-ui, sans-serif; }\n .rendered-body { font-size: 10px; }\n .rendered-nav-link { font-size: 9px; }\n .none-block { display: none; font-size: 10px; }\n .hidden-block { visibility: hidden; font-size: 10px; }\n\nstyle=\"font-size: 10px\"","classText":"none-block\nhidden-block\nrendered-body\nrendered-nav-link"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Numbered Section Labels Fixture</title>\n <style>\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #ffffff;\n color: #222222;\n }\n .column { width: 520px; padding: 20px; }\n h2 { font-size: 28px; margin: 0 0 12px; }\n h3 { font-size: 22px; margin: 0 0 10px; }\n section { margin-bottom: 48px; }\n\n /* ── flag column styles ── */\n .sec-index {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n .sec-index-micro {\n font-family: monospace;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 2px;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n\n /* ── pass column styles ── */\n .big-index {\n font-size: 16px;\n font-weight: 700;\n font-family: monospace;\n display: block;\n }\n .plain-note {\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n color: #666666;\n display: block;\n }\n .step-label {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n display: block;\n }\n .card {\n border: 1px solid #d4d4d4;\n border-radius: 8px;\n padding: 16px;\n }\n </style>\n</head>\n<body>\n\n <!-- ══ SHOULD FLAG: tiny numeric index labels repeated across sections ══ -->\n <div class=\"column\">\n\n <section>\n <span class=\"sec-index\">01</span>\n <h2>Alpha ships first</h2>\n <p>Direct previous-sibling label: tiny, mono, bold, zero-padded index.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">02</span>\n <h2>Beta earns trust</h2>\n <p>Second section repeating the same index scaffold.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">03</span>\n <div>\n <h2>Gamma holds the line</h2>\n <p>Label precedes the wrapper; the heading leads the wrapper.</p>\n </div>\n </section>\n\n <section>\n <span class=\"sec-index-micro\">04 / ROLLOUT</span>\n <h2>Delta closes the loop</h2>\n <p>Index joined to a tracked micro-label by a separator glyph.</p>\n </section>\n\n </div>\n\n <!-- ══ SHOULD PASS ══ -->\n <div class=\"column\">\n\n <section>\n <span class=\"big-index\">05</span>\n <h2>Epsilon reads large</h2>\n <p>16px index is a deliberate display device, not a micro label.</p>\n </section>\n\n <section>\n <span class=\"plain-note\">12 · minute read</span>\n <h2>Zeta stays plain</h2>\n <p>Parses as an index but carries no micro-label styling: regular weight, neutral color, no tracking, serif body face.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">Step 6</span>\n <h2>Eta walks steps</h2>\n <p>Word-first label is not a numeric index.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">7</span>\n <h2>Theta counts casually</h2>\n <p>A bare unpadded single digit is ordinary list numbering.</p>\n </section>\n\n <ol>\n <li>\n <span class=\"step-label\">08</span>\n <h3>Iota lives in a list</h3>\n </li>\n <li>\n <span class=\"step-label\">09</span>\n <h3>Kappa lives in a list too</h3>\n </li>\n </ol>\n\n <article class=\"card\">\n <span class=\"sec-index\">10</span>\n <h3>Lambda sits in a card</h3>\n <p>Per-card indices are item numbering, not section scaffolding.</p>\n </article>\n\n <nav aria-label=\"Progress\">\n <span class=\"sec-index\">11</span>\n <h3>Mu tracks progress</h3>\n </nav>\n\n </div>\n\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #ffffff;\n color: #222222;\n }\n .column { width: 520px; padding: 20px; }\n h2 { font-size: 28px; margin: 0 0 12px; }\n h3 { font-size: 22px; margin: 0 0 10px; }\n section { margin-bottom: 48px; }\n\n /* ── flag column styles ── */\n .sec-index {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n .sec-index-micro {\n font-family: monospace;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 2px;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n\n /* ── pass column styles ── */\n .big-index {\n font-size: 16px;\n font-weight: 700;\n font-family: monospace;\n display: block;\n }\n .plain-note {\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n color: #666666;\n display: block;\n }\n .step-label {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n display: block;\n }\n .card {\n border: 1px solid #d4d4d4;\n border-radius: 8px;\n padding: 16px;\n }\n ","classText":"column\nsec-index\nsec-index\nsec-index\nsec-index-micro\ncolumn\nbig-index\nplain-note\nsec-index\nsec-index\nstep-label\nstep-label\ncard\nsec-index\nsec-index"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Numbered section marker fixture</title>\n <style>\n .fixture-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n }\n .case {\n margin: 0 0 18px;\n padding: 16px;\n border: 1px solid #d7d0c2;\n border-radius: 8px;\n }\n .marker {\n display: block;\n font: 700 12px/1.1 ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #7c5c00;\n }\n .code-like::before {\n content: \"01 02 03\";\n }\n </style>\n</head>\n<body>\n <main class=\"fixture-grid\">\n <section aria-labelledby=\"flag-title\">\n <h1 id=\"flag-title\">Should flag</h1>\n <article class=\"case\">\n <span class=\"marker\">01</span>\n <h2>Strategy</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">02</span>\n <h2>Prototype</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">03</span>\n <h2>Launch</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">04</span>\n <h2>Measure</h2>\n </article>\n </section>\n\n <section aria-labelledby=\"pass-title\">\n <h1 id=\"pass-title\">Should pass</h1>\n <article class=\"case\">\n <h2>Readable date</h2>\n <p>Updated June 14, 2026.</p>\n </article>\n <article class=\"case\">\n <h2>Statistic cards</h2>\n <p>Revenue grew 18%, margin reached 24%, and churn fell to 3%.</p>\n </article>\n <article class=\"case\">\n <h2>CSS generated content</h2>\n <p class=\"code-like\">Generated numbers in CSS should not count as body copy.</p>\n </article>\n <article class=\"case\">\n <h2>Inline SVG coordinates</h2>\n <svg width=\"120\" height=\"32\" viewBox=\"0 0 120 32\" aria-hidden=\"true\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" fill=\"none\" stroke=\"#333\" />\n </svg>\n </article>\n <article class=\"case\">\n <h2>Script constants</h2>\n <script>\n const colorStops = ['01', '02', '03', '04'];\n const luminance = 0.2126 + 0.7152 + 0.0722;\n const size = '11.5px';\n </script>\n <p>Implementation constants in script blocks are invisible.</p>\n </article>\n </section>\n </main>\n</body>\n</html>\n",{"styleText":"\n .fixture-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n }\n .case {\n margin: 0 0 18px;\n padding: 16px;\n border: 1px solid #d7d0c2;\n border-radius: 8px;\n }\n .marker {\n display: block;\n font: 700 12px/1.1 ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #7c5c00;\n }\n .code-like::before {\n content: \"01 02 03\";\n }\n ","classText":"fixture-grid\ncase\nmarker\ncase\nmarker\ncase\nmarker\ncase\nmarker\ncase\ncase\ncase\ncode-like\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Overlay Positioning Edge Cases</title>\n <style>\n body {\n font-family: system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n max-width: 900px;\n margin: 0 auto;\n }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n .scenario { margin-bottom: 2rem; padding: 1rem; border: 1px dashed #d1d5db; border-radius: 8px; }\n .scenario-label { font-size: 0.75rem; color: #6b7280; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }\n\n /* ============================================\n 1. TRANSFORM ANCESTOR\n position:absolute overlays on body use document coords,\n but getBoundingClientRect is viewport-relative.\n Transforms on ancestors create new stacking contexts.\n ============================================ */\n .transform-container {\n transform: translateY(0);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-rotate {\n transform: rotate(0deg);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-scale {\n transform: scale(1);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-nested-outer {\n transform: translateX(0);\n padding: 1rem;\n background: #e5e7eb;\n border-radius: 8px;\n }\n .transform-nested-inner {\n transform: translateY(0);\n padding: 1rem;\n background: #d1d5db;\n border-radius: 8px;\n }\n\n /* ============================================\n 2. CLOSED <details>\n Elements inside collapsed details may report\n a bounding rect but aren't visually present.\n ============================================ */\n details { margin-bottom: 1rem; }\n summary { cursor: pointer; font-weight: 600; }\n\n /* ============================================\n 3. STICKY / FIXED POSITIONING\n Sticky elements change their containing block\n behavior during scroll.\n ============================================ */\n .sticky-container {\n height: 200px;\n overflow-y: auto;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n }\n .sticky-header {\n position: sticky;\n top: 0;\n background: #1f2937;\n color: white;\n padding: 0.5rem 1rem;\n z-index: 10;\n }\n .sticky-content {\n padding: 1rem;\n }\n\n /* ============================================\n 4. OVERFLOW HIDDEN\n Elements that extend beyond an overflow:hidden\n parent are visually clipped but still in the DOM.\n ============================================ */\n .overflow-hidden-container {\n overflow: hidden;\n height: 60px;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 0.5rem 1rem;\n }\n\n /* ============================================\n 5. ABSOLUTE / RELATIVE POSITIONING\n Elements that are offset from their normal flow\n position via top/left.\n ============================================ */\n .relative-offset {\n position: relative;\n top: 20px;\n left: 40px;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .absolute-container {\n position: relative;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .absolute-child {\n position: absolute;\n bottom: 10px;\n right: 10px;\n }\n\n /* ============================================\n 6. FLEXBOX / GRID CENTERING\n Elements centered via flex/grid may have\n unexpected positions vs. document flow.\n ============================================ */\n .flex-center {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .grid-center {\n display: grid;\n place-items: center;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n\n /* ============================================\n 7. WILL-CHANGE / CONTAIN / FILTER\n These CSS properties create new containing\n blocks, similar to transforms.\n ============================================ */\n .will-change-container {\n will-change: transform;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .contain-container {\n contain: layout;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .filter-container {\n filter: brightness(1);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .backdrop-filter-container {\n backdrop-filter: blur(0px);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n\n /* ============================================\n 8. MARGIN COLLAPSE / NEGATIVE MARGINS\n Elements with collapsed or negative margins\n can shift from expected position.\n ============================================ */\n .negative-margin-container {\n padding: 2rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .negative-margin-child {\n margin-top: -1rem;\n margin-left: -1rem;\n }\n\n /* ============================================\n Anti-pattern triggers (tiny-text, cramped, ai-color)\n used across scenarios.\n ============================================ */\n .tiny { font-size: 10px; color: #374151; line-height: 1.4; }\n .cramped-box { border: 1px solid #d1d5db; padding: 3px; font-size: 14px; border-radius: 4px; }\n .ai-btn { background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white; padding: 8px 16px; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; }\n .side-tab-card { border-left: 4px solid #8b5cf6; padding: 1rem; background: white; border-radius: 0 8px 8px 0; }\n </style>\n</head>\n<body>\n\n<h1>Overlay Positioning Edge Cases</h1>\n<p>Each scenario places a detectable anti-pattern inside a layout context that can cause overlay misalignment.</p>\n\n<!-- ═══════════════════════════════════════════\n 1. TRANSFORM ANCESTORS\n ═══════════════════════════════════════════ -->\n<h2>1. Transform Ancestors</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">1a. translateY(0) container</div>\n <div class=\"transform-container\">\n <span class=\"tiny\">This tiny text is inside a transform: translateY(0) container. The overlay should frame this text precisely.</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">1b. rotate(0deg) container</div>\n <div class=\"transform-rotate\">\n <span class=\"tiny\">Tiny text inside a transform: rotate(0deg) container.</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">1c. scale(1) container</div>\n <div class=\"transform-scale\">\n <div class=\"cramped-box\">Cramped text inside a transform: scale(1) container.</div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">1d. Nested transforms</div>\n <div class=\"transform-nested-outer\">\n <div class=\"transform-nested-inner\">\n <span class=\"tiny\">Tiny text nested inside two levels of transformed parents.</span>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">1e. Transform + absolute child</div>\n <div class=\"transform-container\" style=\"position: relative; height: 100px;\">\n <div style=\"position: absolute; bottom: 8px; right: 8px;\">\n <span class=\"tiny\">Absolutely positioned tiny text inside transformed parent.</span>\n </div>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 2. CLOSED <details>\n ═══════════════════════════════════════════ -->\n<h2>2. Closed Details</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">2a. Closed details with anti-pattern inside</div>\n <details>\n <summary>Click to expand (closed by default)</summary>\n <span class=\"tiny\">This tiny text is hidden inside a closed details element. No overlay should appear for it.</span>\n <div class=\"cramped-box\">Cramped text also hidden inside closed details.</div>\n </details>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">2b. Open details with anti-pattern inside</div>\n <details open>\n <summary>This details is open</summary>\n <span class=\"tiny\">This tiny text IS visible because details is open. Overlay should frame it correctly.</span>\n </details>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">2c. Nested details (outer open, inner closed)</div>\n <details open>\n <summary>Outer details (open)</summary>\n <p>Some visible content.</p>\n <details>\n <summary>Inner details (closed)</summary>\n <span class=\"tiny\">Hidden tiny text inside nested closed details.</span>\n </details>\n </details>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">2d. Transform inside closed details</div>\n <details>\n <summary>Closed with transform inside</summary>\n <div class=\"transform-container\">\n <span class=\"tiny\">Tiny text inside transform inside closed details. Double trouble.</span>\n </div>\n </details>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 3. STICKY / FIXED\n ═══════════════════════════════════════════ -->\n<h2>3. Sticky and Fixed Positioning</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">3a. Sticky header inside scrollable container</div>\n <div class=\"sticky-container\">\n <div class=\"sticky-header\">\n <span class=\"tiny\">Tiny text in a sticky header.</span>\n </div>\n <div class=\"sticky-content\">\n <p>Scroll this container to test sticky behavior.</p>\n <p>More content to enable scrolling.</p>\n <p>Even more content here.</p>\n <div class=\"cramped-box\">Cramped text below the sticky header.</div>\n <p>Additional content.</p>\n <p>More filler text.</p>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">3b. Fixed footer (stays at bottom of viewport)</div>\n <p>The fixed footer at the bottom of the page has tiny text. Its overlay should stay fixed too.</p>\n</div>\n\n<!-- Actual fixed footer -- outside scenario container so it's truly fixed -->\n<div style=\"position: fixed; bottom: 0; left: 0; right: 0; background: #1f2937; color: white; padding: 0.5rem 1rem; z-index: 50;\">\n <span class=\"tiny\">Tiny text in a fixed footer -- overlay should track this on scroll.</span>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 4. OVERFLOW HIDDEN\n ═══════════════════════════════════════════ -->\n<h2>4. Overflow Hidden</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">4a. Content clipped by overflow:hidden</div>\n <div class=\"overflow-hidden-container\">\n <p>This paragraph is visible.</p>\n <span class=\"tiny\">This tiny text is below the overflow cutoff, clipped but still in DOM.</span>\n <p>This content is also clipped away.</p>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">4b. overflow:hidden + transform ancestor</div>\n <div class=\"transform-container\">\n <div class=\"overflow-hidden-container\">\n <p>Visible inside transform + overflow.</p>\n <span class=\"tiny\">Clipped tiny text inside a transformed overflow:hidden container.</span>\n </div>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 5. ABSOLUTE / RELATIVE OFFSETS\n ═══════════════════════════════════════════ -->\n<h2>5. Position Offsets</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">5a. Relative position with top/left offset</div>\n <div class=\"relative-offset\">\n <span class=\"tiny\">This tiny text is shifted via position:relative + top/left offset.</span>\n </div>\n <div style=\"height: 30px;\"></div><!-- spacer for the offset -->\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">5b. Absolute child in relative parent</div>\n <div class=\"absolute-container\">\n <p>Normal flow content at top.</p>\n <div class=\"absolute-child\">\n <span class=\"tiny\">Absolutely positioned tiny text at bottom-right.</span>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">5c. Negative top offset</div>\n <div style=\"padding-top: 2rem;\">\n <div style=\"position: relative; top: -1rem;\">\n <span class=\"tiny\">Tiny text pulled upward via negative top offset.</span>\n </div>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 6. FLEX / GRID CENTERING\n ═══════════════════════════════════════════ -->\n<h2>6. Flex and Grid Centering</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">6a. Flex-centered anti-pattern</div>\n <div class=\"flex-center\">\n <span class=\"tiny\">Centered tiny text inside a flex container.</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">6b. Grid-centered anti-pattern</div>\n <div class=\"grid-center\">\n <div class=\"cramped-box\">Cramped text inside a grid-centered container.</div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">6c. Flex with transform</div>\n <div class=\"flex-center transform-container\">\n <button class=\"ai-btn\">AI Gradient Button in Flex + Transform</button>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 7. WILL-CHANGE / CONTAIN / FILTER\n ═══════════════════════════════════════════ -->\n<h2>7. Containing Block Creators</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">7a. will-change: transform</div>\n <div class=\"will-change-container\">\n <span class=\"tiny\">Tiny text inside a will-change: transform container.</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">7b. contain: layout</div>\n <div class=\"contain-container\">\n <span class=\"tiny\">Tiny text inside a contain: layout container.</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">7c. filter: brightness(1)</div>\n <div class=\"filter-container\">\n <span class=\"tiny\">Tiny text inside a filter container (creates containing block).</span>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">7d. backdrop-filter</div>\n <div class=\"backdrop-filter-container\">\n <span class=\"tiny\">Tiny text inside a backdrop-filter container.</span>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 8. NEGATIVE MARGINS\n ═══════════════════════════════════════════ -->\n<h2>8. Margin Collapse and Negative Margins</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">8a. Negative margin shifting element position</div>\n <div class=\"negative-margin-container\">\n <div class=\"negative-margin-child\">\n <span class=\"tiny\">Tiny text pulled out of its parent via negative margins.</span>\n </div>\n </div>\n</div>\n\n<!-- ═══════════════════════════════════════════\n 9. COMBINATION SCENARIOS\n ═══════════════════════════════════════════ -->\n<h2>9. Combined Edge Cases</h2>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">9a. Transform + sticky + overflow</div>\n <div class=\"transform-container\">\n <div class=\"sticky-container\">\n <div class=\"sticky-header\">\n <span class=\"tiny\">Tiny text in sticky header inside transform inside scrollable overflow.</span>\n </div>\n <div class=\"sticky-content\">\n <p>Scroll content line 1.</p>\n <p>Scroll content line 2.</p>\n <p>Scroll content line 3.</p>\n <div class=\"cramped-box\">Cramped box scrolled below sticky.</div>\n <p>Scroll content line 4.</p>\n <p>Scroll content line 5.</p>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">9b. Closed details + transform + flex</div>\n <details>\n <summary>Closed combo scenario</summary>\n <div class=\"flex-center transform-container\">\n <span class=\"tiny\">Triple-nested: closed details > flex > transform > tiny text.</span>\n </div>\n </details>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">9c. Grid + absolute + transform</div>\n <div class=\"grid-center\" style=\"position: relative; height: 150px;\">\n <div class=\"transform-container\" style=\"position: absolute; top: 10px; left: 10px; right: 10px;\">\n <span class=\"tiny\">Absolute inside grid inside transform: overlays must track all three contexts.</span>\n </div>\n <div style=\"position: absolute; bottom: 10px; right: 10px;\">\n <div class=\"cramped-box\">Absolute cramped box at bottom-right of grid.</div>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">9d. Overflow hidden + absolute breakout</div>\n <div style=\"position: relative; overflow: hidden; height: 80px; background: #f3f4f6; border-radius: 8px; padding: 1rem;\">\n <p>Visible content.</p>\n <div style=\"position: absolute; bottom: -20px; left: 0; right: 0;\">\n <span class=\"tiny\">Absolute-positioned tiny text that breaks out below overflow:hidden.</span>\n </div>\n </div>\n</div>\n\n<div class=\"scenario\">\n <div class=\"scenario-label\">9e. Side-tab card inside transform + relative offset</div>\n <div class=\"transform-container\">\n <div style=\"position: relative; left: 50px;\">\n <div class=\"side-tab-card\">\n This card has a side-tab border accent and is offset inside a transform container.\n </div>\n </div>\n </div>\n</div>\n\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n max-width: 900px;\n margin: 0 auto;\n }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n .scenario { margin-bottom: 2rem; padding: 1rem; border: 1px dashed #d1d5db; border-radius: 8px; }\n .scenario-label { font-size: 0.75rem; color: #6b7280; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }\n\n /* ============================================\n 1. TRANSFORM ANCESTOR\n position:absolute overlays on body use document coords,\n but getBoundingClientRect is viewport-relative.\n Transforms on ancestors create new stacking contexts.\n ============================================ */\n .transform-container {\n transform: translateY(0);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-rotate {\n transform: rotate(0deg);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-scale {\n transform: scale(1);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .transform-nested-outer {\n transform: translateX(0);\n padding: 1rem;\n background: #e5e7eb;\n border-radius: 8px;\n }\n .transform-nested-inner {\n transform: translateY(0);\n padding: 1rem;\n background: #d1d5db;\n border-radius: 8px;\n }\n\n /* ============================================\n 2. CLOSED <details>\n Elements inside collapsed details may report\n a bounding rect but aren't visually present.\n ============================================ */\n details { margin-bottom: 1rem; }\n summary { cursor: pointer; font-weight: 600; }\n\n /* ============================================\n 3. STICKY / FIXED POSITIONING\n Sticky elements change their containing block\n behavior during scroll.\n ============================================ */\n .sticky-container {\n height: 200px;\n overflow-y: auto;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n }\n .sticky-header {\n position: sticky;\n top: 0;\n background: #1f2937;\n color: white;\n padding: 0.5rem 1rem;\n z-index: 10;\n }\n .sticky-content {\n padding: 1rem;\n }\n\n /* ============================================\n 4. OVERFLOW HIDDEN\n Elements that extend beyond an overflow:hidden\n parent are visually clipped but still in the DOM.\n ============================================ */\n .overflow-hidden-container {\n overflow: hidden;\n height: 60px;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n padding: 0.5rem 1rem;\n }\n\n /* ============================================\n 5. ABSOLUTE / RELATIVE POSITIONING\n Elements that are offset from their normal flow\n position via top/left.\n ============================================ */\n .relative-offset {\n position: relative;\n top: 20px;\n left: 40px;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .absolute-container {\n position: relative;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .absolute-child {\n position: absolute;\n bottom: 10px;\n right: 10px;\n }\n\n /* ============================================\n 6. FLEXBOX / GRID CENTERING\n Elements centered via flex/grid may have\n unexpected positions vs. document flow.\n ============================================ */\n .flex-center {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .grid-center {\n display: grid;\n place-items: center;\n height: 120px;\n background: #f3f4f6;\n border-radius: 8px;\n }\n\n /* ============================================\n 7. WILL-CHANGE / CONTAIN / FILTER\n These CSS properties create new containing\n blocks, similar to transforms.\n ============================================ */\n .will-change-container {\n will-change: transform;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .contain-container {\n contain: layout;\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .filter-container {\n filter: brightness(1);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .backdrop-filter-container {\n backdrop-filter: blur(0px);\n padding: 1rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n\n /* ============================================\n 8. MARGIN COLLAPSE / NEGATIVE MARGINS\n Elements with collapsed or negative margins\n can shift from expected position.\n ============================================ */\n .negative-margin-container {\n padding: 2rem;\n background: #f3f4f6;\n border-radius: 8px;\n }\n .negative-margin-child {\n margin-top: -1rem;\n margin-left: -1rem;\n }\n\n /* ============================================\n Anti-pattern triggers (tiny-text, cramped, ai-color)\n used across scenarios.\n ============================================ */\n .tiny { font-size: 10px; color: #374151; line-height: 1.4; }\n .cramped-box { border: 1px solid #d1d5db; padding: 3px; font-size: 14px; border-radius: 4px; }\n .ai-btn { background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white; padding: 8px 16px; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; }\n .side-tab-card { border-left: 4px solid #8b5cf6; padding: 1rem; background: white; border-radius: 0 8px 8px 0; }\n \nstyle=\"position: relative; height: 100px;\"\nstyle=\"position: absolute; bottom: 8px; right: 8px;\"\nstyle=\"position: fixed; bottom: 0; left: 0; right: 0; background: #1f2937; color: white; padding: 0.5rem 1rem; z-index: 50;\"\nstyle=\"height: 30px;\"\nstyle=\"padding-top: 2rem;\"\nstyle=\"position: relative; top: -1rem;\"\nstyle=\"position: relative; height: 150px;\"\nstyle=\"position: absolute; top: 10px; left: 10px; right: 10px;\"\nstyle=\"position: absolute; bottom: 10px; right: 10px;\"\nstyle=\"position: relative; overflow: hidden; height: 80px; background: #f3f4f6; border-radius: 8px; padding: 1rem;\"\nstyle=\"position: absolute; bottom: -20px; left: 0; right: 0;\"\nstyle=\"position: relative; left: 50px;\"","classText":"scenario\nscenario-label\ntransform-container\ntiny\nscenario\nscenario-label\ntransform-rotate\ntiny\nscenario\nscenario-label\ntransform-scale\ncramped-box\nscenario\nscenario-label\ntransform-nested-outer\ntransform-nested-inner\ntiny\nscenario\nscenario-label\ntransform-container\ntiny\nscenario\nscenario-label\ntiny\ncramped-box\nscenario\nscenario-label\ntiny\nscenario\nscenario-label\ntiny\nscenario\nscenario-label\ntransform-container\ntiny\nscenario\nscenario-label\nsticky-container\nsticky-header\ntiny\nsticky-content\ncramped-box\nscenario\nscenario-label\ntiny\nscenario\nscenario-label\noverflow-hidden-container\ntiny\nscenario\nscenario-label\ntransform-container\noverflow-hidden-container\ntiny\nscenario\nscenario-label\nrelative-offset\ntiny\nscenario\nscenario-label\nabsolute-container\nabsolute-child\ntiny\nscenario\nscenario-label\ntiny\nscenario\nscenario-label\nflex-center\ntiny\nscenario\nscenario-label\ngrid-center\ncramped-box\nscenario\nscenario-label\nflex-center transform-container\nai-btn\nscenario\nscenario-label\nwill-change-container\ntiny\nscenario\nscenario-label\ncontain-container\ntiny\nscenario\nscenario-label\nfilter-container\ntiny\nscenario\nscenario-label\nbackdrop-filter-container\ntiny\nscenario\nscenario-label\nnegative-margin-container\nnegative-margin-child\ntiny\nscenario\nscenario-label\ntransform-container\nsticky-container\nsticky-header\ntiny\nsticky-content\ncramped-box\nscenario\nscenario-label\nflex-center transform-container\ntiny\nscenario\nscenario-label\ngrid-center\ntransform-container\ntiny\ncramped-box\nscenario\nscenario-label\ntiny\nscenario\nscenario-label\ntransform-container\nside-tab-card"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":".ai-btn"}]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Oversized H1 browser fixture</title>\n <style>\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { display: grid; gap: 48px; padding: 32px; }\n h1 { margin: 0; line-height: 1.04; }\n .flag-dominating-h1 {\n max-width: 1040px;\n font-size: 104px;\n }\n .pass-contained-large-h1 {\n max-width: 1120px;\n font-size: 83px;\n }\n </style>\n</head>\n<body>\n <main>\n <h1 class=\"flag-dominating-h1\">A sweeping product headline that sprawls across the whole viewport and keeps going</h1>\n <h1 class=\"pass-contained-large-h1\">The missing design vocabulary for agents.</h1>\n </main>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { display: grid; gap: 48px; padding: 32px; }\n h1 { margin: 0; line-height: 1.04; }\n .flag-dominating-h1 {\n max-width: 1040px;\n font-size: 104px;\n }\n .pass-contained-large-h1 {\n max-width: 1120px;\n font-size: 83px;\n }\n ","classText":"flag-dominating-h1\npass-contained-large-h1"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Oversized H1 fixture</title>\n <style>\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n h1 { margin: 0 0 24px; line-height: 1.05; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: a long headline set at display size, dominating the viewport -->\n <section class=\"col flag\">\n <h1 style=\"font-size: 96px\">A sweeping product headline that runs on far too long to sit at this size</h1>\n <h1 style=\"font-size: 80px\">Everything your whole team needs to launch quickly and finally rest at night</h1>\n </section>\n\n <!-- PASS column: punchy short headlines at display size (a valid choice), and a long headline at a sane size -->\n <section class=\"col pass\">\n <h1 style=\"font-size: 168px\">Bold.</h1>\n <h1 style=\"font-size: 120px\">Ship faster</h1>\n <h1 style=\"font-size: 40px\">A perfectly ordinary headline that is reasonably long but set at a sane, readable size</h1>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n h1 { margin: 0 0 24px; line-height: 1.05; }\n \nstyle=\"font-size: 96px\"\nstyle=\"font-size: 80px\"\nstyle=\"font-size: 168px\"\nstyle=\"font-size: 120px\"\nstyle=\"font-size: 40px\"","classText":"cols\ncol flag\ncol pass"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html>\n<head>\n<title>Pseudo-element side stripe fixture</title>\n<style>\n :root {\n --ok: oklch(0.78 0.145 155);\n --line: rgba(0, 0, 0, 0.08);\n }\n\n /* FLAG: classic ::before accent stripe — absolute, full height, left edge,\n chromatic background resolved through a custom property. */\n .card-stripe { position: relative; border-radius: 12px; }\n .card-stripe::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 5px;\n background: var(--ok);\n }\n\n /* FLAG: right-edge variant using inset shorthand + literal color. */\n .row-stripe { position: relative; }\n .row-stripe::after {\n content: \"\";\n position: absolute;\n inset: 0 0 0 auto;\n width: 4px;\n background-color: #3b82f6;\n }\n\n /* PASS: 1px neutral hairline divider (below width threshold + neutral). */\n .col + .col::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 1px;\n background: var(--line);\n }\n\n /* PASS: neutral 4px rail (timeline spine) — chromatic gate rejects it. */\n .timeline::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n background: rgb(210, 210, 214);\n }\n\n /* PASS: blockquote decoration is exempt in prose contexts. */\n blockquote::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n background: #d97706;\n }\n\n /* PASS: toggle knob — offset from the edge, not a stripe. */\n .switch::before {\n content: \"\";\n position: absolute;\n left: 2px; top: 2px;\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #3b82f6;\n }\n\n /* PASS: wide decorative panel, not a stripe. */\n .panel::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 40px;\n background: #3b82f6;\n }\n\n /* PASS: horizontal underline accent (not full height, not edge-anchored\n vertically). */\n .heading::after {\n content: \"\";\n position: absolute;\n left: 0; bottom: 0;\n width: 6px; height: 3px;\n background: #3b82f6;\n }\n</style>\n</head>\n<body>\n <div class=\"card-stripe\">Card with pseudo stripe</div>\n <div class=\"row-stripe\">Row with right stripe</div>\n <div class=\"col\">One</div><div class=\"col\">Two</div>\n <div class=\"timeline\">Timeline</div>\n <blockquote>Quoted text</blockquote>\n <span class=\"switch\"></span>\n <div class=\"panel\">Panel</div>\n <h2 class=\"heading\">Heading</h2>\n</body>\n</html>\n",{"styleText":"\n :root {\n --ok: oklch(0.78 0.145 155);\n --line: rgba(0, 0, 0, 0.08);\n }\n\n /* FLAG: classic ::before accent stripe — absolute, full height, left edge,\n chromatic background resolved through a custom property. */\n .card-stripe { position: relative; border-radius: 12px; }\n .card-stripe::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 5px;\n background: var(--ok);\n }\n\n /* FLAG: right-edge variant using inset shorthand + literal color. */\n .row-stripe { position: relative; }\n .row-stripe::after {\n content: \"\";\n position: absolute;\n inset: 0 0 0 auto;\n width: 4px;\n background-color: #3b82f6;\n }\n\n /* PASS: 1px neutral hairline divider (below width threshold + neutral). */\n .col + .col::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 1px;\n background: var(--line);\n }\n\n /* PASS: neutral 4px rail (timeline spine) — chromatic gate rejects it. */\n .timeline::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n background: rgb(210, 210, 214);\n }\n\n /* PASS: blockquote decoration is exempt in prose contexts. */\n blockquote::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n background: #d97706;\n }\n\n /* PASS: toggle knob — offset from the edge, not a stripe. */\n .switch::before {\n content: \"\";\n position: absolute;\n left: 2px; top: 2px;\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #3b82f6;\n }\n\n /* PASS: wide decorative panel, not a stripe. */\n .panel::before {\n content: \"\";\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 40px;\n background: #3b82f6;\n }\n\n /* PASS: horizontal underline accent (not full height, not edge-anchored\n vertically). */\n .heading::after {\n content: \"\";\n position: absolute;\n left: 0; bottom: 0;\n width: 6px; height: 3px;\n background: #3b82f6;\n }\n","classText":"card-stripe\nrow-stripe\ncol\ncol\ntimeline\nswitch\npanel\nheading"}],"result":[{"id":"side-tab","snippet":".card-stripe::before — absolute 5px pseudo-element stripe (left: 0)","index":287,"selector":".card-stripe::before"},{"id":"side-tab","snippet":".row-stripe::after — absolute 4px pseudo-element stripe (right: 0)","index":543,"selector":".row-stripe::after"}]}
{"args":["<!DOCTYPE html>\n<html>\n<head>\n<title>Pulsing status dot fixture</title>\n<style>\n :root { --dot: 8px; }\n\n /* FLAG: tiny circular live dot on an infinite opacity pulse. */\n .live-dot {\n width: 7px; height: 7px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2.4s ease-out infinite;\n }\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n /* FLAG: box-shadow ripple variant, var-sized, pill radius. */\n .status .dot {\n width: var(--dot); height: var(--dot);\n border-radius: 999px;\n background: #ef4444;\n animation: ripple 1.8s linear infinite;\n }\n @keyframes ripple {\n 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }\n 100% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }\n }\n\n /* FLAG: longhand animation-name + iteration-count. */\n .beacon {\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #f59e0b;\n animation-name: pulse;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n }\n\n /* PASS: rotation-only spinner — same size class, must never flag. */\n .spinner {\n width: 14px; height: 14px;\n border-radius: 50%;\n border: 2px solid #e5e7eb;\n border-top-color: #3b82f6;\n animation: spin 0.8s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* PASS: named pulse but the local keyframes only rotate. */\n .fake-pulse {\n width: 8px; height: 8px;\n border-radius: 50%;\n animation: pulse-ring 1s linear infinite;\n }\n @keyframes pulse-ring { to { transform: rotate(180deg); } }\n\n /* PASS: large breathing card — not a dot. */\n .breathing-card {\n width: 240px; height: 120px;\n border-radius: 16px;\n animation: pulse 3s ease-in-out infinite;\n }\n\n /* PASS: finite attention pulse (no infinite). */\n .nudge {\n width: 8px; height: 8px;\n border-radius: 50%;\n animation: pulse 0.6s ease-out 3;\n }\n\n /* PASS: square badge, not circular. */\n .square-badge {\n width: 12px; height: 12px;\n border-radius: 2px;\n animation: pulse 2s infinite;\n }\n\n /* FLAG (error): header logo dot whose size and animation live in\n separate rule blocks, animation added inside a matching media query,\n with a reduced-motion reset that must not mask the default. */\n .rec-mark {\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #dc2626;\n }\n @media (prefers-reduced-motion: no-preference) {\n .rec-mark { animation: pulse 2.4s ease-in-out infinite; }\n }\n @media (prefers-reduced-motion: reduce) {\n .rec-mark { animation: none; }\n }\n</style>\n</head>\n<body>\n <header>\n <a href=\"#top\"><span class=\"rec-mark\"></span> Brand</a>\n </header>\n <span class=\"live-dot\"></span>\n <span class=\"status\"><i class=\"dot\"></i> Online</span>\n <span class=\"beacon\"></span>\n <span class=\"spinner\"></span>\n <span class=\"fake-pulse\"></span>\n <div class=\"breathing-card\">Card</div>\n <span class=\"nudge\"></span>\n <span class=\"square-badge\"></span>\n <!-- FLAG: Tailwind utility variant -->\n <span class=\"animate-ping w-2 h-2 rounded-full bg-emerald-500\"></span>\n <!-- PASS: Tailwind pulse on a large skeleton block -->\n <div class=\"animate-pulse rounded-md w-48 h-6 bg-gray-200\"></div>\n</body>\n</html>\n",{"styleText":"\n :root { --dot: 8px; }\n\n /* FLAG: tiny circular live dot on an infinite opacity pulse. */\n .live-dot {\n width: 7px; height: 7px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2.4s ease-out infinite;\n }\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n /* FLAG: box-shadow ripple variant, var-sized, pill radius. */\n .status .dot {\n width: var(--dot); height: var(--dot);\n border-radius: 999px;\n background: #ef4444;\n animation: ripple 1.8s linear infinite;\n }\n @keyframes ripple {\n 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }\n 100% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }\n }\n\n /* FLAG: longhand animation-name + iteration-count. */\n .beacon {\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #f59e0b;\n animation-name: pulse;\n animation-duration: 2s;\n animation-iteration-count: infinite;\n }\n\n /* PASS: rotation-only spinner — same size class, must never flag. */\n .spinner {\n width: 14px; height: 14px;\n border-radius: 50%;\n border: 2px solid #e5e7eb;\n border-top-color: #3b82f6;\n animation: spin 0.8s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* PASS: named pulse but the local keyframes only rotate. */\n .fake-pulse {\n width: 8px; height: 8px;\n border-radius: 50%;\n animation: pulse-ring 1s linear infinite;\n }\n @keyframes pulse-ring { to { transform: rotate(180deg); } }\n\n /* PASS: large breathing card — not a dot. */\n .breathing-card {\n width: 240px; height: 120px;\n border-radius: 16px;\n animation: pulse 3s ease-in-out infinite;\n }\n\n /* PASS: finite attention pulse (no infinite). */\n .nudge {\n width: 8px; height: 8px;\n border-radius: 50%;\n animation: pulse 0.6s ease-out 3;\n }\n\n /* PASS: square badge, not circular. */\n .square-badge {\n width: 12px; height: 12px;\n border-radius: 2px;\n animation: pulse 2s infinite;\n }\n\n /* FLAG (error): header logo dot whose size and animation live in\n separate rule blocks, animation added inside a matching media query,\n with a reduced-motion reset that must not mask the default. */\n .rec-mark {\n width: 10px; height: 10px;\n border-radius: 50%;\n background: #dc2626;\n }\n @media (prefers-reduced-motion: no-preference) {\n .rec-mark { animation: pulse 2.4s ease-in-out infinite; }\n }\n @media (prefers-reduced-motion: reduce) {\n .rec-mark { animation: none; }\n }\n","classText":"rec-mark\nlive-dot\nstatus\ndot\nbeacon\nspinner\nfake-pulse\nbreathing-card\nnudge\nsquare-badge\nanimate-ping w-2 h-2 rounded-full bg-emerald-500\nanimate-pulse rounded-md w-48 h-6 bg-gray-200"}],"result":[{"id":"pulsing-dot","snippet":".live-dot — 7x7px dot with infinite \"pulse\" animation","selector":".live-dot"},{"id":"pulsing-dot","snippet":".status .dot — 8x8px dot with infinite \"ripple\" animation","selector":".status .dot"},{"id":"pulsing-dot","snippet":".beacon — 10x10px dot with infinite \"pulse\" animation","selector":".beacon"},{"id":"pulsing-dot","snippet":".rec-mark — 10x10px dot with infinite \"pulse\" animation in header/nav","selector":".rec-mark","severity":"error"},{"id":"pulsing-dot","snippet":"animate-ping on tiny rounded-full element"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Quality (Typography & Readability) — Should Flag vs Should Pass</title>\n <style>\n /* Two-column fixture: left = should flag, right = should pass.\n Covers the typography-quality rules that need real browser layout\n (line-length, tight-leading, tiny-text, justified-text, all-caps-body,\n wide-tracking, skipped-heading). All are browser-only — see\n tests/detect-antipatterns-browser.test.mjs for assertions. */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.6; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .case { margin-bottom: 16px; padding: 16px; background: white; border: 1px solid #e2e8f0; border-radius: 8px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 6px; font-style: italic; }\n\n /* ── FLAG: typography quality issues ── */\n\n /* Line length: 12px body text fits >85 chars/line in a ~528px column.\n 12px is not below the tiny-text 12px floor (rule fires at <12px). */\n .long-lines {\n font-size: 12px;\n line-height: 1.6;\n }\n\n /* Tight leading */\n .tight-leading {\n font-size: 16px;\n line-height: 1.0;\n max-width: 40em;\n }\n\n /* Tiny body text */\n .tiny-text {\n font-size: 10px;\n line-height: 1.6;\n max-width: 40em;\n }\n .tiny-text-11 {\n font-size: 11px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text without hyphens */\n .justified-no-hyphens {\n text-align: justify;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* All-caps long body */\n .all-caps-body {\n text-transform: uppercase;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* Wide letter spacing on body */\n .wide-tracking-body {\n letter-spacing: 0.15em;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* ── PASS: comfortable typography ── */\n\n .good-measure {\n max-width: 65ch;\n font-size: 16px;\n line-height: 1.6;\n }\n\n .good-leading {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n .good-text-size {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text WITH hyphens — fine */\n .justified-with-hyphens {\n text-align: justify;\n hyphens: auto;\n -webkit-hyphens: auto;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* Short label in all-caps (passes — under 30 chars) */\n .label-caps {\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.1em;\n color: #475569;\n }\n\n /* Wide tracking on a short uppercase label (passes — both conditions excluded) */\n .label-tracking {\n letter-spacing: 0.15em;\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n }\n\n .mock-terminal-title,\n .mock-terminal-meta,\n .mock-diff-line {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: #475569;\n line-height: 1.5;\n }\n .mock-terminal-title { font-size: 11px; }\n .mock-terminal-meta { font-size: 10.8px; }\n .mock-diff-line { font-size: 11.84px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Line length too long</h3>\n <div class=\"case\">\n <span class=\"case-label\">no max-width on a paragraph</span>\n <p class=\"long-lines\">This paragraph has no max-width constraint at all, which means on a wide monitor or ultrawide display, each line of text can stretch to 150 or even 200 characters wide. Research consistently shows that line lengths beyond 75 characters significantly reduce reading speed and comprehension. The eye has to travel too far to find the beginning of the next line, causing readers to lose their place. A simple max-width of 65ch to 75ch on the paragraph or its container would fix this entirely.</p>\n </div>\n\n <h3>Tight line height</h3>\n <div class=\"case\">\n <span class=\"case-label\">line-height: 1.0</span>\n <p class=\"tight-leading\">This paragraph has a line-height of 1.0, which means the lines are touching. Multi-line body text needs breathing room between lines for readability. A line-height of 1.5 to 1.7 is generally recommended for body text.</p>\n </div>\n\n <h3>Tiny body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">10px body text</span>\n <p class=\"tiny-text\">This body text is only 10px. While this might be fine for a disclaimer or legal footnote, it's too small for primary content that users need to actually read. Aim for at least 14px for body content, 16px is ideal.</p>\n </div>\n\n <h3>Justified text without hyphens</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-align: justify, no hyphens: auto</span>\n <p class=\"justified-no-hyphens\">This paragraph uses text-align: justify, which forces each line to stretch to fill the full width. Without hyphenation support, this creates uneven gaps between words known as \"rivers of white space\" that flow vertically through the text. Left-aligned text is almost always more readable on the web.</p>\n </div>\n\n <h3>All-caps body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-transform: uppercase on a long passage</span>\n <p class=\"all-caps-body\">This entire paragraph is in uppercase via text-transform. While all-caps works for short labels, headings, or navigation items, longer body text in uppercase is significantly harder to read because we lose the word shape cues that come from ascenders and descenders in mixed-case text.</p>\n </div>\n\n <h3>Wide letter spacing on body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">letter-spacing: 0.15em on body text</span>\n <p class=\"wide-tracking-body\">This body text has letter-spacing: 0.15em applied to it. While subtle tracking adjustments can improve readability for headings or all-caps text, adding significant letter spacing to body text actually makes it harder to read by disrupting natural character groupings.</p>\n </div>\n\n <h3>Skipped heading levels</h3>\n <div class=\"case\">\n <span class=\"case-label\">h1 → h3 (missing h2)</span>\n <h1 style=\"font-size: 20px; margin: 0 0 4px;\">Top heading</h1>\n <h3 style=\"font-size: 16px; margin: 0;\">Skips straight to h3</h3>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Comfortable line length</h3>\n <div class=\"case\">\n <span class=\"case-label\">max-width: 65ch on the paragraph</span>\n <p class=\"good-measure\">This paragraph has a max-width of 65ch, keeping the line length comfortable for reading. The eye can easily track from the end of one line to the beginning of the next without losing its place.</p>\n </div>\n\n <h3>Comfortable line height</h3>\n <div class=\"case\">\n <span class=\"case-label\">line-height: 1.6</span>\n <p class=\"good-leading\">This paragraph has a line-height of 1.6, which gives multi-line text plenty of room to breathe and improves the rhythm of the page.</p>\n </div>\n\n <h3>Comfortable body text size</h3>\n <div class=\"case\">\n <span class=\"case-label\">16px body text</span>\n <p class=\"good-text-size\">This is 16px body text — the recommended baseline for comfortable reading on modern displays.</p>\n </div>\n\n <h3>Justified text with hyphens</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-align: justify + hyphens: auto</span>\n <p class=\"justified-with-hyphens\">When justified text is paired with hyphens: auto, the browser can break long words across lines, eliminating the rivers of white space and making the justification look intentional rather than awkward.</p>\n </div>\n\n <h3>Short label in all-caps</h3>\n <div class=\"case\">\n <span class=\"case-label\">short label, uppercase</span>\n <span class=\"label-caps\">Featured</span>\n </div>\n\n <h3>Wide tracking on a short uppercase label</h3>\n <div class=\"case\">\n <span class=\"case-label\">letter-spacing on a short uppercase label</span>\n <span class=\"label-tracking\">Beta</span>\n </div>\n\n <h3>Proper heading hierarchy</h3>\n <div class=\"case\">\n <span class=\"case-label\">h1 → h2 → h3</span>\n <h1 style=\"font-size: 20px; margin: 0 0 4px;\">Top heading</h1>\n <h2 style=\"font-size: 18px; margin: 0 0 4px;\">Second level</h2>\n <h3 style=\"font-size: 16px; margin: 0;\">Third level</h3>\n </div>\n\n <h3>Small mockup metadata</h3>\n <div class=\"case\">\n <span class=\"case-label\">terminal/diff metadata at tiny sizes</span>\n <span class=\"mock-terminal-title\">/impeccable polish : scanning codebase</span>\n <div class=\"mock-terminal-meta\">12 files inspected · 4 visual issues queued</div>\n <div class=\"mock-diff-line\">+ Replace generic button label with action-specific copy</div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n <!-- SVG text: Chrome reports fake scrollWidth/clientWidth on SVG content;\n the text-overflow rule must skip it (pass). -->\n <svg class=\"svg-metrics-pass\" viewBox=\"0 0 430 60\" width=\"430\" height=\"60\" role=\"img\" aria-label=\"diagram\">\n <rect x=\"6\" y=\"10\" width=\"96\" height=\"40\" rx=\"3\" fill=\"none\" stroke=\"#888\"/>\n <text x=\"54\" y=\"35\" text-anchor=\"middle\" font-size=\"13\">impeccable</text>\n </svg>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left = should flag, right = should pass.\n Covers the typography-quality rules that need real browser layout\n (line-length, tight-leading, tiny-text, justified-text, all-caps-body,\n wide-tracking, skipped-heading). All are browser-only — see\n tests/detect-antipatterns-browser.test.mjs for assertions. */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.6; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .case { margin-bottom: 16px; padding: 16px; background: white; border: 1px solid #e2e8f0; border-radius: 8px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 6px; font-style: italic; }\n\n /* ── FLAG: typography quality issues ── */\n\n /* Line length: 12px body text fits >85 chars/line in a ~528px column.\n 12px is not below the tiny-text 12px floor (rule fires at <12px). */\n .long-lines {\n font-size: 12px;\n line-height: 1.6;\n }\n\n /* Tight leading */\n .tight-leading {\n font-size: 16px;\n line-height: 1.0;\n max-width: 40em;\n }\n\n /* Tiny body text */\n .tiny-text {\n font-size: 10px;\n line-height: 1.6;\n max-width: 40em;\n }\n .tiny-text-11 {\n font-size: 11px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text without hyphens */\n .justified-no-hyphens {\n text-align: justify;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* All-caps long body */\n .all-caps-body {\n text-transform: uppercase;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* Wide letter spacing on body */\n .wide-tracking-body {\n letter-spacing: 0.15em;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* ── PASS: comfortable typography ── */\n\n .good-measure {\n max-width: 65ch;\n font-size: 16px;\n line-height: 1.6;\n }\n\n .good-leading {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n .good-text-size {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text WITH hyphens — fine */\n .justified-with-hyphens {\n text-align: justify;\n hyphens: auto;\n -webkit-hyphens: auto;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* Short label in all-caps (passes — under 30 chars) */\n .label-caps {\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.1em;\n color: #475569;\n }\n\n /* Wide tracking on a short uppercase label (passes — both conditions excluded) */\n .label-tracking {\n letter-spacing: 0.15em;\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n }\n\n .mock-terminal-title,\n .mock-terminal-meta,\n .mock-diff-line {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: #475569;\n line-height: 1.5;\n }\n .mock-terminal-title { font-size: 11px; }\n .mock-terminal-meta { font-size: 10.8px; }\n .mock-diff-line { font-size: 11.84px; }\n \nstyle=\"font-size: 20px; margin: 0 0 4px;\"\nstyle=\"font-size: 16px; margin: 0;\"\nstyle=\"font-size: 20px; margin: 0 0 4px;\"\nstyle=\"font-size: 18px; margin: 0 0 4px;\"\nstyle=\"font-size: 16px; margin: 0;\"","classText":"grid\ncol\ncase\ncase-label\nlong-lines\ncase\ncase-label\ntight-leading\ncase\ncase-label\ntiny-text\ncase\ncase-label\njustified-no-hyphens\ncase\ncase-label\nall-caps-body\ncase\ncase-label\nwide-tracking-body\ncase\ncase-label\ncol\ncase\ncase-label\ngood-measure\ncase\ncase-label\ngood-leading\ncase\ncase-label\ngood-text-size\ncase\ncase-label\njustified-with-hyphens\ncase\ncase-label\nlabel-caps\ncase\ncase-label\nlabel-tracking\ncase\ncase-label\ncase\ncase-label\nmock-terminal-title\nmock-terminal-meta\nmock-diff-line\nsvg-metrics-pass"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>radial-spotlight-glow fixture</title>\n<style>\n /* jsdom / static-html engine does no layout: every case declares explicit\n pixel width + height so the size gate can read them. */\n body { margin: 0; background: #0b0d13; }\n .grid { display: flex; flex-wrap: wrap; gap: 24px; padding: 24px; }\n .case { color: #e6e6e6; }\n\n /* ───────── SHOULD FLAG: decorative low-opacity chromatic spotlights ───────── */\n\n /* The exact declaration that evaded the detector: soft blue spotlight fading\n to transparent, painted on a large mobile hero. */\n .flag-hero-blue {\n width: 360px; height: 560px;\n background: radial-gradient(circle at 52% 38%, rgba(80,111,255,0.26), transparent 44%);\n }\n /* Violet accent wash on a full section. */\n .flag-section-violet {\n width: 900px; height: 600px;\n background-image: radial-gradient(circle, rgba(139,92,246,0.30), transparent 60%);\n }\n /* Cyan overlay-style glow (the pseudo-element overlay div reflex). */\n .flag-overlay-cyan {\n width: 760px; height: 480px;\n background: radial-gradient(ellipse at top, rgba(34,211,238,0.22), transparent 70%);\n }\n /* Single glow drawn with two low-alpha stops of one hue fading out. */\n .flag-two-stop-rose {\n width: 640px; height: 400px;\n background: radial-gradient(circle, rgba(255,90,120,0.28) 0%, rgba(255,90,120,0.12) 45%, transparent 75%);\n }\n /* Same tell written as an 8-digit hex (#506fff at alpha 0x3d ≈ 0.24). */\n .flag-hex-alpha {\n width: 700px; height: 460px;\n background: radial-gradient(circle, #506fff3d, transparent 50%);\n }\n\n /* ───────── SHOULD PASS: real backgrounds, small elements, neutrals, rich art ───────── */\n\n /* Opaque radial background — a real surface, not a glow (no transparent end). */\n .pass-opaque-bg {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #1e3a8a, #0f172a);\n }\n /* Small badge: chromatic low-alpha radial glow, but tiny. */\n .pass-badge {\n width: 40px; height: 40px;\n background: radial-gradient(circle, rgba(80,111,255,0.30), transparent);\n }\n /* Avatar-scale light: the subject IS a small light, exempt by size. */\n .pass-avatar-light {\n width: 72px; height: 72px;\n background: radial-gradient(circle, rgba(255,196,64,0.35), transparent 70%);\n }\n /* Neutral near-black vignette — grayscale, not the accent-glow tell. */\n .pass-neutral-vignette {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(0,0,0,0.40), transparent 80%);\n }\n /* Neutral near-white vignette — grayscale, exempt. */\n .pass-white-vignette {\n width: 820px; height: 540px;\n background: radial-gradient(ellipse, rgba(255,255,255,0.35), transparent 70%);\n }\n /* Rich three-color radial composition — real color structure, opaque, no fade. */\n .pass-rich-composition {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #ff0080 0%, #7928ca 45%, #0070f3 100%);\n }\n /* Rich composition that does fade out, but its color stops are opaque. */\n .pass-rich-transparent {\n width: 780px; height: 500px;\n background: radial-gradient(circle, rgba(255,0,128,0.90) 0%, rgba(121,40,202,0.85) 50%, transparent);\n }\n /* Opaque center glow (the radial-halo case, alpha ≥ 0.45) — not this rule. */\n .pass-opaque-center {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(80,111,255,0.85), transparent);\n }\n /* Low-alpha chromatic wash, but LINEAR, not radial. */\n .pass-linear-wash {\n width: 800px; height: 520px;\n background: linear-gradient(120deg, rgba(80,111,255,0.28), transparent 60%);\n }\n</style>\n</head>\n<body>\n <main class=\"grid\">\n <!-- should-flag column -->\n <div class=\"case flag-hero-blue\" data-name=\"Hero Spotlight Blue\"></div>\n <div class=\"case flag-section-violet\" data-name=\"Section Glow Violet\"></div>\n <div class=\"case flag-overlay-cyan\" data-name=\"Overlay Glow Cyan\"></div>\n <div class=\"case flag-two-stop-rose\" data-name=\"Two Stop Soft Glow\"></div>\n <div class=\"case flag-hex-alpha\" data-name=\"Hex Alpha Glow\"></div>\n\n <!-- should-pass column -->\n <div class=\"case pass-opaque-bg\" data-name=\"Opaque Radial Background\"></div>\n <div class=\"case pass-badge\" data-name=\"Small Accent Badge\"></div>\n <div class=\"case pass-avatar-light\" data-name=\"Avatar Glow Light\"></div>\n <div class=\"case pass-neutral-vignette\" data-name=\"Neutral Vignette\"></div>\n <div class=\"case pass-white-vignette\" data-name=\"White Vignette\"></div>\n <div class=\"case pass-rich-composition\" data-name=\"Rich Radial Composition\"></div>\n <div class=\"case pass-rich-transparent\" data-name=\"Rich Transparent Composition\"></div>\n <div class=\"case pass-opaque-center\" data-name=\"Opaque Center Glow\"></div>\n <div class=\"case pass-linear-wash\" data-name=\"Linear Gradient Wash\"></div>\n </main>\n</body>\n</html>\n",{"styleText":"\n /* jsdom / static-html engine does no layout: every case declares explicit\n pixel width + height so the size gate can read them. */\n body { margin: 0; background: #0b0d13; }\n .grid { display: flex; flex-wrap: wrap; gap: 24px; padding: 24px; }\n .case { color: #e6e6e6; }\n\n /* ───────── SHOULD FLAG: decorative low-opacity chromatic spotlights ───────── */\n\n /* The exact declaration that evaded the detector: soft blue spotlight fading\n to transparent, painted on a large mobile hero. */\n .flag-hero-blue {\n width: 360px; height: 560px;\n background: radial-gradient(circle at 52% 38%, rgba(80,111,255,0.26), transparent 44%);\n }\n /* Violet accent wash on a full section. */\n .flag-section-violet {\n width: 900px; height: 600px;\n background-image: radial-gradient(circle, rgba(139,92,246,0.30), transparent 60%);\n }\n /* Cyan overlay-style glow (the pseudo-element overlay div reflex). */\n .flag-overlay-cyan {\n width: 760px; height: 480px;\n background: radial-gradient(ellipse at top, rgba(34,211,238,0.22), transparent 70%);\n }\n /* Single glow drawn with two low-alpha stops of one hue fading out. */\n .flag-two-stop-rose {\n width: 640px; height: 400px;\n background: radial-gradient(circle, rgba(255,90,120,0.28) 0%, rgba(255,90,120,0.12) 45%, transparent 75%);\n }\n /* Same tell written as an 8-digit hex (#506fff at alpha 0x3d ≈ 0.24). */\n .flag-hex-alpha {\n width: 700px; height: 460px;\n background: radial-gradient(circle, #506fff3d, transparent 50%);\n }\n\n /* ───────── SHOULD PASS: real backgrounds, small elements, neutrals, rich art ───────── */\n\n /* Opaque radial background — a real surface, not a glow (no transparent end). */\n .pass-opaque-bg {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #1e3a8a, #0f172a);\n }\n /* Small badge: chromatic low-alpha radial glow, but tiny. */\n .pass-badge {\n width: 40px; height: 40px;\n background: radial-gradient(circle, rgba(80,111,255,0.30), transparent);\n }\n /* Avatar-scale light: the subject IS a small light, exempt by size. */\n .pass-avatar-light {\n width: 72px; height: 72px;\n background: radial-gradient(circle, rgba(255,196,64,0.35), transparent 70%);\n }\n /* Neutral near-black vignette — grayscale, not the accent-glow tell. */\n .pass-neutral-vignette {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(0,0,0,0.40), transparent 80%);\n }\n /* Neutral near-white vignette — grayscale, exempt. */\n .pass-white-vignette {\n width: 820px; height: 540px;\n background: radial-gradient(ellipse, rgba(255,255,255,0.35), transparent 70%);\n }\n /* Rich three-color radial composition — real color structure, opaque, no fade. */\n .pass-rich-composition {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #ff0080 0%, #7928ca 45%, #0070f3 100%);\n }\n /* Rich composition that does fade out, but its color stops are opaque. */\n .pass-rich-transparent {\n width: 780px; height: 500px;\n background: radial-gradient(circle, rgba(255,0,128,0.90) 0%, rgba(121,40,202,0.85) 50%, transparent);\n }\n /* Opaque center glow (the radial-halo case, alpha ≥ 0.45) — not this rule. */\n .pass-opaque-center {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(80,111,255,0.85), transparent);\n }\n /* Low-alpha chromatic wash, but LINEAR, not radial. */\n .pass-linear-wash {\n width: 800px; height: 520px;\n background: linear-gradient(120deg, rgba(80,111,255,0.28), transparent 60%);\n }\n","classText":"grid\ncase flag-hero-blue\ncase flag-section-violet\ncase flag-overlay-cyan\ncase flag-two-stop-rose\ncase flag-hex-alpha\ncase pass-opaque-bg\ncase pass-badge\ncase pass-avatar-light\ncase pass-neutral-vignette\ncase pass-white-vignette\ncase pass-rich-composition\ncase pass-rich-transparent\ncase pass-opaque-center\ncase pass-linear-wash"}],"result":[{"id":"radial-halo","snippet":"radial-gradient halo (#ff0080 → transparent) on dark page","selector":"/* Rich composition that does fade out, but its color stops are opaque. */ .pass-rich-transparent"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Repeated Container Text Fixture</title>\n <style>\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #f5f5f4;\n color: #222222;\n }\n .card {\n background: #ffffff;\n border: 1px solid #d6d3d1;\n border-radius: 10px;\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n .shadow-card {\n background: #ffffff;\n border-radius: 10px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n h2 { font-size: 22px; margin: 0 0 10px; }\n .big-word { font-size: 30px; font-weight: 700; color: #b91c1c; }\n .tag { font-size: 12px; text-transform: uppercase; color: #b91c1c; }\n .row { padding: 6px 0; border-bottom: 1px solid #eeeeee; }\n .status { font-size: 13px; color: #57534e; }\n td, th { padding: 6px 10px; font-size: 14px; }\n </style>\n</head>\n<body>\n\n <!-- ══ SHOULD FLAG ══ -->\n\n <!-- Same word wired into three structurally different slots of one card -->\n <div class=\"card\" id=\"flag-status-card\">\n <h2>Museumplein departure</h2>\n <div class=\"headline\"><span class=\"big-word\">Suspended</span></div>\n <div class=\"meta\"><span class=\"tag\">Suspended</span></div>\n <p class=\"note\">This service is <strong>Suspended</strong> until further notice.</p>\n </div>\n\n <!-- Four distinct slots inside one elevated card -->\n <div class=\"shadow-card\" id=\"flag-ticket-card\">\n <h2>Evening show</h2>\n <div class=\"banner\"><span class=\"big-word\">Unavailable</span></div>\n <div class=\"detail\"><em class=\"tag\">Unavailable</em></div>\n <div class=\"footer\"><span class=\"status\">Unavailable</span></div>\n <p class=\"aside\">Currently <strong>Unavailable</strong> at this venue.</p>\n </div>\n\n <!-- ══ SHOULD PASS ══ -->\n\n <!-- Table columns inside a card repeat by design -->\n <div class=\"card\" id=\"pass-table-card\">\n <h2>Deploy history</h2>\n <table>\n <tr><td>build 12</td><td>Rolled back</td></tr>\n <tr><td>build 13</td><td>Rolled back</td></tr>\n <tr><td>build 14</td><td>Rolled back</td></tr>\n </table>\n </div>\n\n <!-- Parallel templated rows: identical structural position each time -->\n <div class=\"card\" id=\"pass-parallel-rows\">\n <h2>Departures</h2>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n </div>\n\n <!-- Nav list inside a card -->\n <div class=\"card\" id=\"pass-nav-card\">\n <h2>Sections</h2>\n <nav>\n <div class=\"a\"><span>Overview page</span></div>\n <div class=\"b\"><em>Overview page</em></div>\n <div class=\"c\"><strong>Overview page</strong></div>\n </nav>\n </div>\n\n <!-- Short strings never count -->\n <div class=\"card\" id=\"pass-short-text\">\n <h2>Toggles</h2>\n <div class=\"one\"><span>Off</span></div>\n <div class=\"two\"><em>Off</em></div>\n <div class=\"three\"><strong>Off</strong></div>\n </div>\n\n <!-- Digits-only strings never count -->\n <div class=\"card\" id=\"pass-numbers\">\n <h2>Scores</h2>\n <div class=\"one\"><span>2026</span></div>\n <div class=\"two\"><em>2026</em></div>\n <div class=\"three\"><strong>2026</strong></div>\n </div>\n\n <!-- One occurrence per card, three cards -->\n <div class=\"card\" id=\"pass-spread-a\"><span class=\"tag\">Standby mode</span></div>\n <div class=\"card\" id=\"pass-spread-b\"><em class=\"tag\">Standby mode</em></div>\n <div class=\"card\" id=\"pass-spread-c\"><strong class=\"tag\">Standby mode</strong></div>\n\n <!-- Calendar-style grid repeats by design -->\n <div class=\"card\" id=\"pass-grid-card\">\n <h2>Availability</h2>\n <div role=\"grid\">\n <div class=\"cell-a\"><span>Open slot</span></div>\n <div class=\"cell-b\"><em>Open slot</em></div>\n <div class=\"cell-c\"><strong>Open slot</strong></div>\n </div>\n </div>\n\n <!-- Only two structurally distinct occurrences: below threshold -->\n <div class=\"card\" id=\"pass-two-spots\">\n <h2>Corner case</h2>\n <div class=\"headline\"><span class=\"big-word\">Rescheduled</span></div>\n <div class=\"meta\"><span class=\"tag\">Rescheduled</span></div>\n </div>\n\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #f5f5f4;\n color: #222222;\n }\n .card {\n background: #ffffff;\n border: 1px solid #d6d3d1;\n border-radius: 10px;\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n .shadow-card {\n background: #ffffff;\n border-radius: 10px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n h2 { font-size: 22px; margin: 0 0 10px; }\n .big-word { font-size: 30px; font-weight: 700; color: #b91c1c; }\n .tag { font-size: 12px; text-transform: uppercase; color: #b91c1c; }\n .row { padding: 6px 0; border-bottom: 1px solid #eeeeee; }\n .status { font-size: 13px; color: #57534e; }\n td, th { padding: 6px 10px; font-size: 14px; }\n ","classText":"card\nheadline\nbig-word\nmeta\ntag\nnote\nshadow-card\nbanner\nbig-word\ndetail\ntag\nfooter\nstatus\naside\ncard\ncard\nrow\nstatus\nrow\nstatus\nrow\nstatus\ncard\na\nb\nc\ncard\none\ntwo\nthree\ncard\none\ntwo\nthree\ncard\ntag\ncard\ntag\ncard\ntag\ncard\ncell-a\ncell-b\ncell-c\ncard\nheadline\nbig-word\nmeta\ntag"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Working reveal-on-scroll fixture (browser-only pass case)</title>\n <style>\n html { scroll-behavior: smooth; }\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 24px; }\n section { margin: 0 0 480px; }\n .reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.4s ease-out, transform 0.4s ease-out; }\n .reveal.in-view { opacity: 1; transform: none; }\n .menu { display: none; }\n </style>\n</head>\n<body>\n <main>\n <h1>Visible hero headline stays readable</h1>\n <p>Identical markup to the broken fixture, but the script parses and runs, so every\n reveal section becomes visible once the detector's reveal sweep scrolls it into view.\n Nothing on this page may produce a script-error or content-hidden finding.</p>\n\n <!-- Legitimately hidden UI: excluded from the measurement entirely -->\n <nav class=\"menu\">\n <a href=\"#one\">A menu that only opens on demand</a>\n <a href=\"#two\">Another menu entry with text</a>\n </nav>\n <div hidden>Hidden attribute content that never counts against the page.</div>\n <div aria-hidden=\"true\">Decorative aria-hidden copy that also stays out of the denominator.</div>\n <template><p>Template content never renders and never counts.</p></template>\n\n <section class=\"reveal\" id=\"one\">\n <h2>First revealed section</h2>\n <p>This block starts at opacity zero, exactly like a broken page would, but its\n IntersectionObserver is alive. During the sweep it gains the in-view class and\n transitions to full opacity, so the post-sweep measurement sees it as visible.</p>\n </section>\n <section class=\"reveal\" id=\"two\">\n <h2>Second revealed section</h2>\n <p>More reveal-on-scroll copy that carries a meaningful share of the page's\n character count. If the sweep or the exclusion rules regress, this fixture is the\n page that starts producing a false content-hidden-at-rest finding.</p>\n </section>\n <section class=\"reveal\">\n <h2>Third revealed section</h2>\n <p>The smooth scroll-behavior on the root element is deliberate: the sweep must\n use instant scrolling or it never actually reaches these sections before it\n measures, which was the first false-positive mode found while calibrating.</p>\n </section>\n </main>\n\n <script>\n const sections = document.querySelectorAll('.reveal');\n const io = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) { entry.target.classList.add('in-view'); io.unobserve(entry.target); }\n }\n }, { threshold: 0.1 });\n sections.forEach((el) => io.observe(el));\n </script>\n</body>\n</html>\n",{"styleText":"\n html { scroll-behavior: smooth; }\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 24px; }\n section { margin: 0 0 480px; }\n .reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.4s ease-out, transform 0.4s ease-out; }\n .reveal.in-view { opacity: 1; transform: none; }\n .menu { display: none; }\n ","classText":"menu\nreveal\nreveal\nreveal"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Fixture: data-impeccable-ignore scoped waivers</title>\n<style>\n body { margin: 0; padding: 40px; background: #faf8f4; font-family: Arial, sans-serif; color: #262421; }\n /* Every case carries a UNIQUE border-left width so each finding's snippet\n (`border-left: Npx`) attributes to exactly one case. Flag widths: 5, 6,\n 8, 12. Pass widths: 4, 7, 9, 10, 11. */\n .tabbed {\n width: 420px;\n padding: 16px 20px;\n margin: 0 0 24px;\n background: #ffffff;\n border: 1px solid #ddd8d0;\n border-left: 6px solid #b8422e;\n }\n .w4 { border-left-width: 4px; }\n .w5 { border-left-width: 5px; }\n .w7 { border-left-width: 7px; }\n .w8 { border-left-width: 8px; }\n .w9 { border-left-width: 9px; }\n .w10 { border-left-width: 10px; }\n .w11 { border-left-width: 11px; }\n .w12 { border-left-width: 12px; }\n /* Marquee specimen: keyframes + an infinite loop track. The section that\n hosts it carries a marquee waiver; the CSS-scan finding must resolve the\n selector to the live element and honor the ancestor attribute. */\n @keyframes ticker-loop { from { transform: translateX(0); } to { transform: translateX(-50%); } }\n .quote-track { display: flex; width: max-content; animation: ticker-loop 80s linear infinite; }\n .quote-track span { padding: 8px 16px; }\n /* Dead grid CSS: this two-axis hairline grid selector matches nothing on\n this page, so the browser pass must drop the codex-grid finding — the\n pattern never renders here. */\n .never-rendered-grid {\n background:\n linear-gradient(90deg, rgba(200,180,120,0.08) 1px, transparent 1px),\n linear-gradient(180deg, rgba(200,180,120,0.08) 1px, transparent 1px);\n background-size: 24px 24px;\n }\n /* Overshoot bezier inside a keyframe STEP: enclosingCssSelector must refuse\n `to` as a selector (it would match nothing and wrongly drop the finding),\n so this bounce-easing finding stays page-level and is retained. */\n @keyframes springy {\n from { transform: none; }\n to { transform: translateY(-4px); animation-timing-function: cubic-bezier(0.3, 1.8, 0.6, 1); }\n }\n</style>\n</head>\n<body>\n <!-- FLAG 6px — control: no waiver anywhere above. -->\n <article class=\"tabbed control-flags\">The control case has a 6px side-tab accent border and no waiver, so the side-tab rule must flag it.</article>\n\n <!-- FLAG 8px — waiver names a different rule, so side-tab still applies. -->\n <section data-impeccable-ignore=\"pulsing-dot\">\n <article class=\"tabbed w8 flags-other-rule-waived\">The waiver names pulsing-dot, not side-tab, so this 8px case must still flag.</article>\n </section>\n\n <!-- FLAG 12px — waiver sits on a SIBLING, not an ancestor: no effect. -->\n <div data-impeccable-ignore=\"side-tab\"></div>\n <article class=\"tabbed w12 flags-sibling-waiver\">A waiver on a preceding sibling is not an ancestor; this 12px case must still flag.</article>\n\n <!-- FLAG 5px — waiver value misspells the rule id: exact matching only. -->\n <section data-impeccable-ignore=\"side-tabs\">\n <article class=\"tabbed w5 flags-misspelled-rule\">\"side-tabs\" is not a rule id; the 5px case must still flag.</article>\n </section>\n\n <!-- PASS 7px — exact-rule waiver on the container: the subtree is exempt. -->\n <section data-impeccable-ignore=\"side-tab\">\n <article class=\"tabbed w7 pass-exact-rule\">Inside a container waived for exactly side-tab; must not flag.</article>\n </section>\n\n <!-- PASS 9px — two levels below the waiving container. -->\n <section data-impeccable-ignore=\"side-tab\">\n <div>\n <article class=\"tabbed w9 pass-nested-depth\">Two levels below the waiving container; the ancestor walk must still find it.</article>\n </div>\n </section>\n\n <!-- PASS 10px — star waiver: every rule is exempt in the subtree. -->\n <section data-impeccable-ignore=\"*\">\n <article class=\"tabbed w10 pass-star\">Inside a star-waived container; must not flag.</article>\n </section>\n\n <!-- PASS 11px — comma list including side-tab. -->\n <section data-impeccable-ignore=\"pulsing-dot, side-tab\">\n <article class=\"tabbed w11 pass-rule-list\">side-tab appears in the comma list; must not flag.</article>\n </section>\n\n <!-- PASS 4px — waiver on the flagged element itself (self counts). -->\n <article class=\"tabbed w4 pass-self-waiver\" data-impeccable-ignore=\"side-tab\">The waiver sits on the element itself; must not flag.</article>\n\n <!-- Live marquee inside a rule-scoped waiver: the CSS-scan marquee finding\n resolves .quote-track to this element and is waived by the ancestor. -->\n <section data-impeccable-ignore=\"marquee\">\n <div class=\"quote-track\"><span>One voice.</span><span>Another voice.</span><span>One voice.</span><span>Another voice.</span></div>\n </section>\n</body>\n</html>\n",{"styleText":"\n body { margin: 0; padding: 40px; background: #faf8f4; font-family: Arial, sans-serif; color: #262421; }\n /* Every case carries a UNIQUE border-left width so each finding's snippet\n (`border-left: Npx`) attributes to exactly one case. Flag widths: 5, 6,\n 8, 12. Pass widths: 4, 7, 9, 10, 11. */\n .tabbed {\n width: 420px;\n padding: 16px 20px;\n margin: 0 0 24px;\n background: #ffffff;\n border: 1px solid #ddd8d0;\n border-left: 6px solid #b8422e;\n }\n .w4 { border-left-width: 4px; }\n .w5 { border-left-width: 5px; }\n .w7 { border-left-width: 7px; }\n .w8 { border-left-width: 8px; }\n .w9 { border-left-width: 9px; }\n .w10 { border-left-width: 10px; }\n .w11 { border-left-width: 11px; }\n .w12 { border-left-width: 12px; }\n /* Marquee specimen: keyframes + an infinite loop track. The section that\n hosts it carries a marquee waiver; the CSS-scan finding must resolve the\n selector to the live element and honor the ancestor attribute. */\n @keyframes ticker-loop { from { transform: translateX(0); } to { transform: translateX(-50%); } }\n .quote-track { display: flex; width: max-content; animation: ticker-loop 80s linear infinite; }\n .quote-track span { padding: 8px 16px; }\n /* Dead grid CSS: this two-axis hairline grid selector matches nothing on\n this page, so the browser pass must drop the codex-grid finding — the\n pattern never renders here. */\n .never-rendered-grid {\n background:\n linear-gradient(90deg, rgba(200,180,120,0.08) 1px, transparent 1px),\n linear-gradient(180deg, rgba(200,180,120,0.08) 1px, transparent 1px);\n background-size: 24px 24px;\n }\n /* Overshoot bezier inside a keyframe STEP: enclosingCssSelector must refuse\n `to` as a selector (it would match nothing and wrongly drop the finding),\n so this bounce-easing finding stays page-level and is retained. */\n @keyframes springy {\n from { transform: none; }\n to { transform: translateY(-4px); animation-timing-function: cubic-bezier(0.3, 1.8, 0.6, 1); }\n }\n","classText":"tabbed control-flags\ntabbed w8 flags-other-rule-waived\ntabbed w12 flags-sibling-waiver\ntabbed w5 flags-misspelled-rule\ntabbed w7 pass-exact-rule\ntabbed w9 pass-nested-depth\ntabbed w10 pass-star\ntabbed w11 pass-rule-list\ntabbed w4 pass-self-waiver\nquote-track"}],"result":[{"id":"bounce-easing","snippet":"cubic-bezier(0.3, 1.8, 0.6, 1)","selector":{"$undef":true}},{"id":"marquee","snippet":".quote-track — infinite horizontal loop animation \"ticker-loop\"","selector":".quote-track"},{"id":"codex-grid-background","snippet":"two-axis grid-line gradient background","selector":"/* Dead grid CSS: this two-axis hairline grid selector matches nothing on this page, so the browser pass must drop the codex-grid finding — the pattern never renders here. */ .never-rendered-grid"}]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Script error + content hidden fixture (browser-only)</title>\n <style>\n html { scroll-behavior: smooth; }\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 24px; }\n section { margin: 0 0 480px; }\n .reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.4s ease-out, transform 0.4s ease-out; }\n .reveal.in-view { opacity: 1; transform: none; }\n </style>\n</head>\n<body>\n <main>\n <h1>Visible hero headline stays readable</h1>\n <p>The opening paragraph renders normally, exactly like the real broken sample: the\n first viewport looks fine while everything below it never appears.</p>\n\n <section class=\"reveal\">\n <h2>First hidden section</h2>\n <p>This block waits for an IntersectionObserver that will never construct, because\n the single script tag below fails to parse. Its text ships in the document but the\n reveal class never lands, so the reader scrolls through blank space.</p>\n </section>\n <section class=\"reveal\">\n <h2>Second hidden section</h2>\n <p>More shipped-but-invisible copy. Together the hidden sections carry well over\n thirty percent of the character count of the page, which is the failed-reveal\n signature this fixture exists to reproduce for the detector tests.</p>\n </section>\n <section class=\"reveal\">\n <h2>Third hidden section</h2>\n <p>A final block of body copy that stays at opacity zero forever. A healthy page\n with this exact markup reveals every one of these sections during the detector's\n reveal sweep; this one cannot, because its JavaScript died at parse time.</p>\n </section>\n </main>\n\n <script>\n const sections = document.querySelectorAll('.reveal');\n const io = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) { entry.target.classList.add('in-view'); io.unobserve(entry.target); }\n }\n }, { threshold: 0.1 });\n sections.forEach((el) => io.observe(el));\n // Deliberate syntax error: an unterminated string kills the whole block,\n // including the observer wiring above.\n const label = 'unterminated\n </script>\n</body>\n</html>\n",{"styleText":"\n html { scroll-behavior: smooth; }\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 24px; }\n section { margin: 0 0 480px; }\n .reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.4s ease-out, transform 0.4s ease-out; }\n .reveal.in-view { opacity: 1; transform: none; }\n ","classText":"reveal\nreveal\nreveal"}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Regression fixture for issue #407: DOM named-property shadowing.\n\n HTMLFormElement is [LegacyOverrideBuiltIns], so a named control shadows even\n builtin getters: a <form> containing <input name=\"id\"> makes `form.id` return\n the INPUT ELEMENT, not the id string. Reading `.startsWith` on it throws\n \"elId.startsWith is not a function\". Every Shopify product form ships an\n <input name=\"id\"> (the variant id), so this crashed the URL scan of essentially\n every Shopify page. `<input name=\"className\">` shadows `form.className` the same\n way. The detector must read these via getAttribute (immune to shadowing).\n-->\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Shadowed form.id regression fixture</title>\n<style>\n body { font-family: system-ui, sans-serif; background: #ffffff; color: #1a1a1a; margin: 0; padding: 24px; }\n .product { max-width: 640px; margin: 0 auto; }\n h1 { font-size: 28px; margin: 0 0 12px; }\n .price { font-size: 20px; font-weight: 600; }\n form { margin-top: 16px; }\n .add-to-cart { background: #1a1a1a; color: #ffffff; border: 0; padding: 12px 24px; border-radius: 6px; font-size: 16px; cursor: pointer; }\n label { display: block; margin: 8px 0 4px; font-size: 14px; }\n select, input[type=\"number\"] { padding: 8px; font-size: 14px; }\n</style>\n</head>\n<body>\n<main class=\"product\">\n <h1>Impeccable Test Product</h1>\n <p class=\"price\">$49.00</p>\n\n <!-- Shopify-style product form: the <input name=\"id\"> shadows form.id. -->\n <form method=\"post\" action=\"/cart/add\" id=\"product-form\">\n <input type=\"hidden\" name=\"id\" value=\"4001\">\n <input type=\"hidden\" name=\"className\" value=\"variant-default\">\n <label for=\"qty\">Quantity</label>\n <input type=\"number\" id=\"qty\" name=\"quantity\" value=\"1\" min=\"1\">\n <label for=\"variant\">Variant</label>\n <select id=\"variant\" name=\"options[Size]\">\n <option value=\"s\">Small</option>\n <option value=\"m\">Medium</option>\n <option value=\"l\">Large</option>\n </select>\n <button type=\"submit\" class=\"add-to-cart\">Add to cart</button>\n </form>\n</main>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #ffffff; color: #1a1a1a; margin: 0; padding: 24px; }\n .product { max-width: 640px; margin: 0 auto; }\n h1 { font-size: 28px; margin: 0 0 12px; }\n .price { font-size: 20px; font-weight: 600; }\n form { margin-top: 16px; }\n .add-to-cart { background: #1a1a1a; color: #ffffff; border: 0; padding: 12px 24px; border-radius: 6px; font-size: 16px; cursor: pointer; }\n label { display: block; margin: 8px 0 4px; font-size: 14px; }\n select, input[type=\"number\"] { padding: 8px; font-size: 14px; }\n","classText":"product\nprice\nadd-to-cart"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Shape-assembled illustration fixture</title>\n</head>\n<body>\n<header>\n <!-- PASS: logo, small (explicit size) even with a big viewBox -->\n <svg viewBox=\"0 0 400 400\" width=\"32\" height=\"32\" aria-hidden=\"true\">\n <rect x=\"0\" y=\"0\" width=\"180\" height=\"180\" fill=\"#c33\"/>\n <rect x=\"220\" y=\"0\" width=\"180\" height=\"180\" fill=\"#3c3\"/>\n <rect x=\"0\" y=\"220\" width=\"180\" height=\"180\" fill=\"#33c\"/>\n <rect x=\"220\" y=\"220\" width=\"180\" height=\"180\" fill=\"#cc3\"/>\n <circle cx=\"200\" cy=\"200\" r=\"60\" fill=\"#333\"/>\n <circle cx=\"200\" cy=\"200\" r=\"30\" fill=\"#eee\"/>\n <polygon points=\"10,10 30,10 20,30\" fill=\"#933\"/>\n <polygon points=\"370,10 390,10 380,30\" fill=\"#393\"/>\n </svg>\n <span>Acme</span>\n</header>\n<main>\n <!-- FLAG: hero mascot scene assembled from primitives at hero size -->\n <svg class=\"mascot\" viewBox=\"0 0 640 480\" role=\"img\" aria-label=\"A cheerful robot waving\">\n <rect x=\"200\" y=\"120\" width=\"240\" height=\"200\" rx=\"24\" fill=\"#8ecae6\"/>\n <rect x=\"240\" y=\"340\" width=\"60\" height=\"100\" fill=\"#219ebc\"/>\n <rect x=\"340\" y=\"340\" width=\"60\" height=\"100\" fill=\"#219ebc\"/>\n <circle cx=\"270\" cy=\"200\" r=\"24\" fill=\"#ffffff\"/>\n <circle cx=\"370\" cy=\"200\" r=\"24\" fill=\"#ffffff\"/>\n <circle cx=\"270\" cy=\"200\" r=\"10\" fill=\"#023047\"/>\n <circle cx=\"370\" cy=\"200\" r=\"10\" fill=\"#023047\"/>\n <ellipse cx=\"320\" cy=\"270\" rx=\"50\" ry=\"20\" fill=\"#ffb703\"/>\n <polygon points=\"180,160 140,120 180,120\" fill=\"#fb8500\"/>\n <polygon points=\"460,160 500,120 460,120\" fill=\"#fb8500\"/>\n <rect x=\"300\" y=\"60\" width=\"8\" height=\"60\" fill=\"#023047\"/>\n <circle cx=\"304\" cy=\"52\" r=\"12\" fill=\"#fb8500\"/>\n </svg>\n\n <!-- PASS: bar chart, data-bearing (axis labels) -->\n <svg viewBox=\"0 0 600 400\" role=\"img\" aria-label=\"Monthly signups\">\n <rect x=\"60\" y=\"220\" width=\"40\" height=\"120\" fill=\"#4477aa\"/>\n <rect x=\"120\" y=\"180\" width=\"40\" height=\"160\" fill=\"#4477aa\"/>\n <rect x=\"180\" y=\"140\" width=\"40\" height=\"200\" fill=\"#66ccee\"/>\n <rect x=\"240\" y=\"200\" width=\"40\" height=\"140\" fill=\"#66ccee\"/>\n <rect x=\"300\" y=\"100\" width=\"40\" height=\"240\" fill=\"#228833\"/>\n <rect x=\"360\" y=\"90\" width=\"40\" height=\"250\" fill=\"#228833\"/>\n <rect x=\"420\" y=\"60\" width=\"40\" height=\"280\" fill=\"#ccbb44\"/>\n <rect x=\"480\" y=\"40\" width=\"40\" height=\"300\" fill=\"#ccbb44\"/>\n <line x1=\"40\" y1=\"340\" x2=\"560\" y2=\"340\" stroke=\"#333\"/>\n <text x=\"60\" y=\"370\">Jan</text>\n <text x=\"180\" y=\"370\">Mar</text>\n <text x=\"300\" y=\"370\">May</text>\n <text x=\"420\" y=\"370\">Jul</text>\n </svg>\n\n <!-- PASS: stroke-only technical line drawing, no fills -->\n <svg viewBox=\"0 0 600 470\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"4\">\n <circle cx=\"200\" cy=\"200\" r=\"80\"/>\n <circle cx=\"200\" cy=\"200\" r=\"40\"/>\n <ellipse cx=\"400\" cy=\"220\" rx=\"90\" ry=\"40\"/>\n <rect x=\"120\" y=\"320\" width=\"360\" height=\"60\"/>\n <rect x=\"150\" y=\"340\" width=\"80\" height=\"20\"/>\n <rect x=\"260\" y=\"340\" width=\"80\" height=\"20\"/>\n <polygon points=\"500,80 540,140 460,140\"/>\n <polygon points=\"80,80 120,140 40,140\"/>\n <path d=\"M120 120 C 200 40, 400 40, 480 120\"/>\n </svg>\n\n <!-- PASS: full-bleed tiling background texture -->\n <svg width=\"100%\" height=\"600\" viewBox=\"0 0 1200 600\" aria-hidden=\"true\">\n <defs>\n <pattern id=\"tile\" width=\"40\" height=\"40\" patternUnits=\"userSpaceOnUse\">\n <rect width=\"20\" height=\"20\" fill=\"#eee\"/>\n <rect x=\"20\" y=\"20\" width=\"20\" height=\"20\" fill=\"#ddd\"/>\n <circle cx=\"30\" cy=\"10\" r=\"4\" fill=\"#ccc\"/>\n </pattern>\n </defs>\n <rect width=\"1200\" height=\"600\" fill=\"url(#tile)\"/>\n <rect x=\"0\" y=\"0\" width=\"600\" height=\"300\" fill=\"url(#tile)\"/>\n <rect x=\"600\" y=\"300\" width=\"600\" height=\"300\" fill=\"url(#tile)\"/>\n <circle cx=\"300\" cy=\"300\" r=\"80\" fill=\"url(#tile)\"/>\n <circle cx=\"900\" cy=\"150\" r=\"60\" fill=\"url(#tile)\"/>\n <circle cx=\"150\" cy=\"450\" r=\"50\" fill=\"url(#tile)\"/>\n <polygon points=\"700,100 760,180 640,180\" fill=\"url(#tile)\"/>\n <polygon points=\"1000,400 1060,480 940,480\" fill=\"url(#tile)\"/>\n </svg>\n\n <!-- PASS: small inline icon -->\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"M8 12l3 3 5-6\"/>\n </svg>\n</main>\n</body>\n</html>\n",{"styleText":"","classText":"mascot"}],"result":[{"id":"shape-assembled-illustration","snippet":"inline <svg> scene: 12 primitive shapes, ~640x480px, 6 fill colors"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Anti-Patterns That Should Be Flagged</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 0.875rem; margin: 2.5rem 0 0.75rem; color: #4b5563; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.08em; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .cards { display: grid; gap: 1rem; max-width: 28rem; }\n .card { background: white; padding: 1rem; border-radius: 0.375rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .card h3 { font-weight: 600; color: #111827; font-size: 0.875rem; }\n .card p { font-size: 0.8125rem; color: #6b7280; margin-top: 0.25rem; }\n\n /* CSS side-tab variants */\n .card-shorthand-left { border-left: 4px solid #3b82f6; }\n .card-shorthand-right { border-right: 5px solid #8b5cf6; }\n .card-longhand-left { border-left-width: 3px; border-left-style: solid; border-left-color: #10b981; }\n .card-longhand-right { border-right-width: 6px; border-right-style: solid; border-right-color: #ef4444; }\n\n /* CSS top/bottom + border-radius */\n .card-css-top { border-radius: 12px; border-top: 4px solid #3b82f6; }\n .card-css-bottom { border-radius: 12px; border-bottom: 3px solid #8b5cf6; }\n </style>\n</head>\n<body>\n <h1>Anti-Patterns: Should Flag</h1>\n <p class=\"intro\">Every example on this page should be detected by the anti-pattern scanner.</p>\n\n <!-- TAILWIND SIDE-TAB -->\n <h2>Tailwind Side-Tab</h2>\n <div class=\"cards\">\n <div class=\"border-l-4 border-blue-500 bg-white p-4 rounded-r shadow-sm\">\n <h3>border-l-4 + rounded-r</h3>\n <p>The classic AI tell.</p>\n </div>\n <div class=\"border-r-4 border-purple-500 bg-white p-4 rounded-l shadow-sm\">\n <h3>border-r-4 + rounded-l</h3>\n <p>Right side variant.</p>\n </div>\n <div class=\"border-e-8 border-red-500 bg-white p-4 rounded-l shadow-sm\">\n <h3>border-e-8 + rounded-l</h3>\n <p>Logical inline-end, extra thick.</p>\n </div>\n </div>\n\n <!-- CSS SIDE-TAB -->\n <h2>CSS Side-Tab</h2>\n <div class=\"cards\">\n <div class=\"card card-shorthand-left\">\n <h3>border-left: 4px solid</h3>\n <p>CSS shorthand.</p>\n </div>\n <div class=\"card card-shorthand-right\">\n <h3>border-right: 5px solid</h3>\n <p>CSS shorthand right.</p>\n </div>\n <div class=\"card card-longhand-left\">\n <h3>border-left-width: 3px</h3>\n <p>CSS longhand.</p>\n </div>\n <div class=\"card card-longhand-right\">\n <h3>border-right-width: 6px</h3>\n <p>CSS longhand right.</p>\n </div>\n </div>\n\n <!-- TOP/BOTTOM + ROUNDED -->\n <h2>Top/Bottom + Rounded</h2>\n <div class=\"cards\">\n <div class=\"border-t-4 border-blue-500 rounded-lg bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-t-4 + rounded-lg</h3>\n <p class=\"text-sm text-gray-600\">Top accent on rounded card.</p>\n </div>\n <div class=\"border-b-4 border-purple-500 rounded-xl bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-b-4 + rounded-xl</h3>\n <p class=\"text-sm text-gray-600\">Bottom accent on rounded card.</p>\n </div>\n <div class=\"border-t-2 border-emerald-500 rounded-md bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-t-2 + rounded-md</h3>\n <p class=\"text-sm text-gray-600\">Even thin top border on rounded.</p>\n </div>\n <div class=\"card card-css-top\">\n <h3>CSS border-top + border-radius</h3>\n <p>Top border from style block.</p>\n </div>\n <div class=\"card card-css-bottom\">\n <h3>CSS border-bottom + border-radius</h3>\n <p>Bottom border from style block.</p>\n </div>\n </div>\n <!-- DARK MODE VARIANTS -->\n <h2>Dark Mode</h2>\n <div class=\"cards\">\n <div class=\"border-l-4 border-cyan-400 rounded-lg bg-gray-800 p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm text-white\">Dark card + border-l-4 + rounded</h3>\n <p class=\"text-sm text-gray-400\">Dark background doesn't make the side-tab OK.</p>\n </div>\n <div class=\"border-t-2 border-violet-500 rounded-xl bg-slate-900 p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm text-white\">Dark card + border-t-2 + rounded</h3>\n <p class=\"text-sm text-gray-400\">Top accent on dark rounded card.</p>\n </div>\n <div style=\"background: #1f2937; padding: 1rem; border-radius: 12px; border-left: 4px solid #f472b6; max-width: 28rem;\">\n <h3 style=\"font-weight: 600; font-size: 0.875rem; color: white;\">Dark CSS card + side border + radius</h3>\n <p style=\"font-size: 0.8125rem; color: #9ca3af;\">Inline dark card with side-tab.</p>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 0.875rem; margin: 2.5rem 0 0.75rem; color: #4b5563; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.08em; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .cards { display: grid; gap: 1rem; max-width: 28rem; }\n .card { background: white; padding: 1rem; border-radius: 0.375rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .card h3 { font-weight: 600; color: #111827; font-size: 0.875rem; }\n .card p { font-size: 0.8125rem; color: #6b7280; margin-top: 0.25rem; }\n\n /* CSS side-tab variants */\n .card-shorthand-left { border-left: 4px solid #3b82f6; }\n .card-shorthand-right { border-right: 5px solid #8b5cf6; }\n .card-longhand-left { border-left-width: 3px; border-left-style: solid; border-left-color: #10b981; }\n .card-longhand-right { border-right-width: 6px; border-right-style: solid; border-right-color: #ef4444; }\n\n /* CSS top/bottom + border-radius */\n .card-css-top { border-radius: 12px; border-top: 4px solid #3b82f6; }\n .card-css-bottom { border-radius: 12px; border-bottom: 3px solid #8b5cf6; }\n \nstyle=\"background: #1f2937; padding: 1rem; border-radius: 12px; border-left: 4px solid #f472b6; max-width: 28rem;\"\nstyle=\"font-weight: 600; font-size: 0.875rem; color: white;\"\nstyle=\"font-size: 0.8125rem; color: #9ca3af;\"","classText":"intro\ncards\nborder-l-4 border-blue-500 bg-white p-4 rounded-r shadow-sm\nborder-r-4 border-purple-500 bg-white p-4 rounded-l shadow-sm\nborder-e-8 border-red-500 bg-white p-4 rounded-l shadow-sm\ncards\ncard card-shorthand-left\ncard card-shorthand-right\ncard card-longhand-left\ncard card-longhand-right\ncards\nborder-t-4 border-blue-500 rounded-lg bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\nborder-b-4 border-purple-500 rounded-xl bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\nborder-t-2 border-emerald-500 rounded-md bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\ncard card-css-top\ncard card-css-bottom\ncards\nborder-l-4 border-cyan-400 rounded-lg bg-gray-800 p-4 shadow-sm\nfont-semibold text-sm text-white\ntext-sm text-gray-400\nborder-t-2 border-violet-500 rounded-xl bg-slate-900 p-4 shadow-sm\nfont-semibold text-sm text-white\ntext-sm text-gray-400"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Clean Patterns — Should NOT Flag</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .section { max-width: 28rem; }\n .card-rounded { background: white; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .card-rounded h3 { font-weight: 600; color: #111827; }\n .card-rounded p { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }\n .card-flat { background: white; padding: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .badge { display: inline-block; font-size: 0.75rem; padding: 0.125rem 0.5rem; border-radius: 9999px; background: #dbeafe; color: #1d4ed8; }\n\n /* Below-threshold side borders without radius */\n .thin-left { border-left: 1px solid #3b82f6; }\n .thin-right { border-right: 2px solid #8b5cf6; }\n </style>\n</head>\n<body>\n <h1>Clean Patterns: Should Pass</h1>\n <p class=\"intro\">None of these should be flagged. They're all intentional, well-established web patterns.</p>\n\n <!-- CLEAN CARDS -->\n <h2>Clean Cards</h2>\n <div class=\"section\">\n <div class=\"card-rounded\" style=\"border: 1px solid #e5e7eb;\">\n <h3>Full border card</h3>\n <p>Subtle 1px border all around. Clean and intentional.</p>\n </div>\n\n <div style=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-top: 1px solid #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n <h3 style=\"font-weight: 600; color: #111827;\">Top border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\">A hairline top rule without rounded corners is a clean section divider.</p>\n </div>\n\n <div style=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-bottom: 2px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n <h3 style=\"font-weight: 600; color: #111827;\">Bottom border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\">Bottom accent without rounded corners is also clean.</p>\n </div>\n\n <div class=\"card-rounded\">\n <span class=\"badge\">New</span>\n <h3>No border at all</h3>\n <p>Just a shadow. Simple and effective.</p>\n </div>\n </div>\n\n <!-- BELOW THRESHOLD -->\n <h2>Below Threshold (Thin, No Radius)</h2>\n <div class=\"section\">\n <div class=\"card-flat thin-left\">\n <p>border-left: 1px solid, no radius — not flagged</p>\n </div>\n <div class=\"card-flat thin-right\">\n <p>border-right: 2px solid, no radius — not flagged</p>\n </div>\n <div class=\"card-flat\" style=\"border-left: 1px solid #10b981;\">\n <p>1px inline border-left, no radius — not flagged</p>\n </div>\n </div>\n <!-- DARK MODE CLEAN -->\n <h2>Dark Mode (Clean)</h2>\n <div class=\"section\">\n <div style=\"background: #1f2937; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #374151; max-width: 28rem; margin-bottom: 1rem;\">\n <h3 style=\"font-weight: 600; color: white;\">Dark card, full border</h3>\n <p style=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\">Uniform 1px border all around. Clean.</p>\n </div>\n <div style=\"background: #111827; padding: 1.5rem; max-width: 28rem; margin-bottom: 1rem; border-bottom: 2px solid #6366f1;\">\n <h3 style=\"font-weight: 600; color: white;\">Dark section, bottom border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\">Bottom accent without radius is fine.</p>\n </div>\n <div style=\"background: #1e293b; padding: 1.5rem; border-radius: 0.5rem; max-width: 28rem; margin-bottom: 1rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3);\">\n <h3 style=\"font-weight: 600; color: white;\">Dark card, no border</h3>\n <p style=\"font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;\">Shadow only. Clean.</p>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .section { max-width: 28rem; }\n .card-rounded { background: white; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .card-rounded h3 { font-weight: 600; color: #111827; }\n .card-rounded p { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }\n .card-flat { background: white; padding: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .badge { display: inline-block; font-size: 0.75rem; padding: 0.125rem 0.5rem; border-radius: 9999px; background: #dbeafe; color: #1d4ed8; }\n\n /* Below-threshold side borders without radius */\n .thin-left { border-left: 1px solid #3b82f6; }\n .thin-right { border-right: 2px solid #8b5cf6; }\n \nstyle=\"border: 1px solid #e5e7eb;\"\nstyle=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-top: 1px solid #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\"\nstyle=\"font-weight: 600; color: #111827;\"\nstyle=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\"\nstyle=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-bottom: 2px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\"\nstyle=\"font-weight: 600; color: #111827;\"\nstyle=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\"\nstyle=\"border-left: 1px solid #10b981;\"\nstyle=\"background: #1f2937; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #374151; max-width: 28rem; margin-bottom: 1rem;\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\"\nstyle=\"background: #111827; padding: 1.5rem; max-width: 28rem; margin-bottom: 1rem; border-bottom: 2px solid #6366f1;\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\"\nstyle=\"background: #1e293b; padding: 1.5rem; border-radius: 0.5rem; max-width: 28rem; margin-bottom: 1rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3);\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;\"","classText":"intro\nsection\ncard-rounded\ncard-rounded\nbadge\nsection\ncard-flat thin-left\ncard-flat thin-right\ncard-flat\nsection"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>text-occlusion fixture</title>\n<style>\n * { margin: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; color: #111; background: #fff; }\n section { padding: 40px; position: relative; }\n\n /* ── FLAG 1: opaque box painted over text (>30%) ── */\n .flag-box-wrap { position: relative; height: 120px; }\n .flag-box-text { position: absolute; top: 40px; left: 40px; font-size: 20px; width: 300px; }\n .flag-box-cover { position: absolute; top: 30px; left: 30px; width: 240px; height: 60px;\n background: #1f7a3d; border-radius: 8px; z-index: 5; }\n\n /* ── FLAG 2: inline element with opaque fill + leaked vertical padding ── */\n .flag-leak-host { position: relative; margin-top: 30px; }\n .flag-leak { display: inline; background: #2f8543; padding: 36px; border-radius: 6px; }\n .flag-leak-card { background: #14202b; color: #fff; padding: 24px; border-radius: 6px; }\n\n /* ── FLAG 3: big headline overhanging an opaque card ── */\n .flag-hero { position: relative; height: 220px; }\n .flag-headline { position: absolute; top: 40px; left: 0; font-size: 64px; font-weight: 800;\n width: 760px; color: #b22; white-space: nowrap; }\n .flag-card { position: absolute; top: 20px; left: 700px; width: 360px; height: 180px;\n background: #f4f5f7; border: 1px solid #ddd; box-shadow: 0 2px 8px rgba(0,0,0,.08); border-radius: 8px; }\n\n /* ── PASS: stacked headline with tight leading (line-box bleed, no real overlap) ── */\n .pass-stack { margin-top: 40px; }\n .pass-eyebrow { font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: #a33; }\n .pass-title { font-size: 96px; line-height: .9; font-weight: 800; }\n\n /* ── PASS: text over image with a scrim, text on top (readable) ── */\n .pass-hero { position: relative; height: 200px; overflow: hidden; border-radius: 8px; }\n .pass-hero img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n .pass-hero .scrim { position: absolute; inset: 0; background: linear-gradient(transparent, rgba(0,0,0,.6)); }\n .pass-hero .cap { position: absolute; bottom: 16px; left: 16px; color: #fff; font-size: 24px; z-index: 2; }\n\n /* ── PASS: fixed status bar over content at the fold ── */\n .pass-under { margin-top: 20px; font-size: 16px; }\n /* Invisible interaction layer: an opacity-0 scrubber input stretched over\n text, like a before/after comparison handle. It paints nothing, so it must\n not read as an occluder despite its UA background-color. */\n .pass-scrubber-wrap { position: relative; margin-top: 30px; height: 80px; }\n .pass-scrubber-text { position: absolute; top: 20px; left: 10px; font-size: 20px; width: 320px; }\n .pass-scrubber-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; z-index: 5; margin: 0; }\n .pass-fixedbar { position: fixed; left: 0; right: 0; bottom: 0; height: 40px; background: #b22;\n color: #fff; display: flex; align-items: center; padding: 0 16px; }\n</style>\n</head>\n<body>\n <section>\n <div class=\"flag-box-wrap\">\n <p class=\"flag-box-text\">Root cause identified in four minutes</p>\n <div class=\"flag-box-cover\" aria-hidden=\"true\"></div>\n </div>\n\n <div class=\"flag-leak-host\">\n <span class=\"flag-leak\" aria-hidden=\"true\"></span>\n <div class=\"flag-leak-card\">Incident closed. Missing index on the replica, found from one alert.</div>\n </div>\n\n <div class=\"flag-hero\">\n <div class=\"flag-headline\">The trace you need is the one they sampled away.</div>\n <div class=\"flag-card\">\n <p>INCIDENT 4417</p>\n <p>service checkout-api</p>\n </div>\n </div>\n\n <div class=\"pass-stack\">\n <p class=\"pass-eyebrow\">Family Italian on the waterfront</p>\n <h1 class=\"pass-title\">Trattoria da Nonna Lucia</h1>\n </div>\n\n <div class=\"pass-hero\">\n <img src=\"data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"\">\n <div class=\"scrim\"></div>\n <div class=\"cap\">Villa on the cliff</div>\n </div>\n\n <p class=\"pass-under\">Every span of every trace, kept on disk, no sampling ever applied here.</p>\n </section>\n <div class=\"pass-fixedbar\">8/8 services up &middot; p99 1.20s &middot; retention infinite</div>\n <div class=\"pass-scrubber-wrap\">\n <p class=\"pass-scrubber-text\">Drag anywhere to compare versions</p>\n <input class=\"pass-scrubber-input\" type=\"range\" min=\"0\" max=\"100\" value=\"50\">\n </div>\n</body>\n</html>\n",{"styleText":"\n * { margin: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; color: #111; background: #fff; }\n section { padding: 40px; position: relative; }\n\n /* ── FLAG 1: opaque box painted over text (>30%) ── */\n .flag-box-wrap { position: relative; height: 120px; }\n .flag-box-text { position: absolute; top: 40px; left: 40px; font-size: 20px; width: 300px; }\n .flag-box-cover { position: absolute; top: 30px; left: 30px; width: 240px; height: 60px;\n background: #1f7a3d; border-radius: 8px; z-index: 5; }\n\n /* ── FLAG 2: inline element with opaque fill + leaked vertical padding ── */\n .flag-leak-host { position: relative; margin-top: 30px; }\n .flag-leak { display: inline; background: #2f8543; padding: 36px; border-radius: 6px; }\n .flag-leak-card { background: #14202b; color: #fff; padding: 24px; border-radius: 6px; }\n\n /* ── FLAG 3: big headline overhanging an opaque card ── */\n .flag-hero { position: relative; height: 220px; }\n .flag-headline { position: absolute; top: 40px; left: 0; font-size: 64px; font-weight: 800;\n width: 760px; color: #b22; white-space: nowrap; }\n .flag-card { position: absolute; top: 20px; left: 700px; width: 360px; height: 180px;\n background: #f4f5f7; border: 1px solid #ddd; box-shadow: 0 2px 8px rgba(0,0,0,.08); border-radius: 8px; }\n\n /* ── PASS: stacked headline with tight leading (line-box bleed, no real overlap) ── */\n .pass-stack { margin-top: 40px; }\n .pass-eyebrow { font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: #a33; }\n .pass-title { font-size: 96px; line-height: .9; font-weight: 800; }\n\n /* ── PASS: text over image with a scrim, text on top (readable) ── */\n .pass-hero { position: relative; height: 200px; overflow: hidden; border-radius: 8px; }\n .pass-hero img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n .pass-hero .scrim { position: absolute; inset: 0; background: linear-gradient(transparent, rgba(0,0,0,.6)); }\n .pass-hero .cap { position: absolute; bottom: 16px; left: 16px; color: #fff; font-size: 24px; z-index: 2; }\n\n /* ── PASS: fixed status bar over content at the fold ── */\n .pass-under { margin-top: 20px; font-size: 16px; }\n /* Invisible interaction layer: an opacity-0 scrubber input stretched over\n text, like a before/after comparison handle. It paints nothing, so it must\n not read as an occluder despite its UA background-color. */\n .pass-scrubber-wrap { position: relative; margin-top: 30px; height: 80px; }\n .pass-scrubber-text { position: absolute; top: 20px; left: 10px; font-size: 20px; width: 320px; }\n .pass-scrubber-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; z-index: 5; margin: 0; }\n .pass-fixedbar { position: fixed; left: 0; right: 0; bottom: 0; height: 40px; background: #b22;\n color: #fff; display: flex; align-items: center; padding: 0 16px; }\n","classText":"flag-box-wrap\nflag-box-text\nflag-box-cover\nflag-leak-host\nflag-leak\nflag-leak-card\nflag-hero\nflag-headline\nflag-card\npass-stack\npass-eyebrow\npass-title\npass-hero\nscrim\ncap\npass-under\npass-fixedbar\npass-scrubber-wrap\npass-scrubber-text\npass-scrubber-input"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Text overflow fixture (browser-only)</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; align-items: start; }\n .col { padding: 16px; }\n .box { width: 160px; border: 1px solid #ddd; margin: 0 0 24px; padding: 8px; }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n white-space: nowrap;\n border-width: 0;\n }\n .sr-only-clip-path { clip-path: inset(50%); }\n .sr-only-legacy-clip { clip: rect(0, 0, 0, 0); }\n .sr-only-tiny-hidden { clip: auto; }\n .sr-only-clipped-wide {\n position: absolute;\n width: 160px;\n height: 20px;\n overflow: visible;\n white-space: nowrap;\n clip-path: inset(50%);\n }\n .hidden-slide {\n opacity: 0;\n pointer-events: none;\n }\n .hidden-slide .pass-hidden-slide-overflow {\n display: block;\n width: 120px;\n white-space: nowrap;\n }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: content forced wider than its box with no scroll affordance -->\n <section class=\"col flag\">\n <div class=\"box flag-nowrap\" style=\"white-space: nowrap\">A single long line of running text that refuses to wrap and clearly spills past its fixed-width box.</div>\n <div class=\"box flag-longword\">Supercalifragilisticexpialidocioussupercalifragilisticexpialidocious</div>\n <!-- Inline owner: the span has no client geometry, so only the rect-vs-container\n path can catch it. The parent box has no direct text and must not flag. -->\n <div class=\"box\"><span class=\"flag-inline-spill\" style=\"white-space: nowrap\">IOC-QARTOD-ARGO-PROFILES-SPILLING-WELL-PAST-THE-CELL</span></div>\n </section>\n\n <!-- PASS column: real scroll regions, preformatted code, wrapping text, scroll ancestors -->\n <section class=\"col pass\">\n <div class=\"box pass-scroll\" style=\"overflow-x: auto; white-space: nowrap\">The very same unwrapping line, but inside a genuine horizontal scroll region meant to scroll.</div>\n <pre class=\"box pass-pre\" style=\"overflow: visible\">a-very-long-preformatted-code-line-that-intentionally-extends-beyond-the-box-width</pre>\n <div class=\"box pass-wrap\">Ordinary wrapping paragraph text that stays comfortably within its container.</div>\n <div class=\"box\" style=\"overflow-x: auto\"><span class=\"pass-inside-scroll\" style=\"white-space: nowrap; display: inline-block\">A long line living inside a scroll ancestor, which must not be flagged.</span></div>\n <span class=\"pass-sr-only-clip-path sr-only sr-only-clip-path\">A screen-reader-only label with long text that should never draw a visible overflow finding.</span>\n <span class=\"pass-sr-only-legacy sr-only sr-only-legacy-clip\">A legacy clipped screen-reader-only label with long text that should not be flagged.</span>\n <span class=\"pass-sr-only-tiny-hidden sr-only sr-only-tiny-hidden\">A tiny overflow-hidden screen-reader-only label without a clip declaration should not be flagged.</span>\n <span class=\"pass-sr-only-clipped-wide sr-only-clipped-wide\">A fully clipped label with a normal-sized box and overflowing text should not be flagged.</span>\n <div class=\"hidden-slide\">\n <span class=\"pass-hidden-slide-overflow\">An inactive carousel slide can contain long text that overflows while hidden.</span>\n </div>\n <div class=\"box\"><span class=\"pass-inline-fits\" style=\"white-space: nowrap\">Fits fine</span></div>\n <div class=\"box\"><span class=\"pass-inline-wraps\">An ordinary inline span with wrapping text that stays inside its container across several lines.</span></div>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; align-items: start; }\n .col { padding: 16px; }\n .box { width: 160px; border: 1px solid #ddd; margin: 0 0 24px; padding: 8px; }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n white-space: nowrap;\n border-width: 0;\n }\n .sr-only-clip-path { clip-path: inset(50%); }\n .sr-only-legacy-clip { clip: rect(0, 0, 0, 0); }\n .sr-only-tiny-hidden { clip: auto; }\n .sr-only-clipped-wide {\n position: absolute;\n width: 160px;\n height: 20px;\n overflow: visible;\n white-space: nowrap;\n clip-path: inset(50%);\n }\n .hidden-slide {\n opacity: 0;\n pointer-events: none;\n }\n .hidden-slide .pass-hidden-slide-overflow {\n display: block;\n width: 120px;\n white-space: nowrap;\n }\n \nstyle=\"white-space: nowrap\"\nstyle=\"white-space: nowrap\"\nstyle=\"overflow-x: auto; white-space: nowrap\"\nstyle=\"overflow: visible\"\nstyle=\"overflow-x: auto\"\nstyle=\"white-space: nowrap; display: inline-block\"\nstyle=\"white-space: nowrap\"","classText":"cols\ncol flag\nbox flag-nowrap\nbox flag-longword\nbox\nflag-inline-spill\ncol pass\nbox pass-scroll\nbox pass-pre\nbox pass-wrap\nbox\npass-inside-scroll\npass-sr-only-clip-path sr-only sr-only-clip-path\npass-sr-only-legacy sr-only sr-only-legacy-clip\npass-sr-only-tiny-hidden sr-only sr-only-tiny-hidden\npass-sr-only-clipped-wide sr-only-clipped-wide\nhidden-slide\npass-hidden-slide-overflow\nbox\npass-inline-fits\nbox\npass-inline-wraps"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography Anti-Patterns — Should Flag</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n font-family: 'Inter', sans-serif;\n background: #f9fafb;\n padding: 2rem;\n }\n h1 { font-size: 18px; font-weight: 700; margin-bottom: 0.5rem; }\n h2 { font-size: 16px; font-weight: 600; margin: 2rem 0 0.75rem; color: #6b7280; }\n h3 { font-size: 15px; font-weight: 600; }\n p { font-size: 14px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 13px; color: #6b7280; }\n </style>\n</head>\n<body>\n <h1>Typography Anti-Patterns</h1>\n <p>This page triggers three typography detections:</p>\n\n <h2>1. Overused Font</h2>\n <p>Inter is loaded from Google Fonts and set as the only font-family. It's the most common AI default.</p>\n\n <h2>2. Single Font</h2>\n <p>There's no second font for headings or display text. Everything uses Inter — no typographic variety.</p>\n\n <h2>3. Flat Type Hierarchy</h2>\n <p>The font sizes are 13px, 14px, 15px, 16px, 18px — all crammed into a 5px range. No visual contrast between heading and body.</p>\n <p class=\"caption\">This caption is barely distinguishable from body text.</p>\n\n <h3>A Subheading</h3>\n <p>Can you tell this is a subheading? Exactly.</p>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: 'Inter', sans-serif;\n background: #f9fafb;\n padding: 2rem;\n }\n h1 { font-size: 18px; font-weight: 700; margin-bottom: 0.5rem; }\n h2 { font-size: 16px; font-weight: 600; margin: 2rem 0 0.75rem; color: #6b7280; }\n h3 { font-size: 15px; font-weight: 600; }\n p { font-size: 14px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 13px; color: #6b7280; }\n ","classText":"caption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography — Clean Patterns</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,700&family=Karla:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n font-family: 'Karla', system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n font-size: 16px;\n color: #111827;\n }\n h1 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 48px;\n font-weight: 700;\n margin-bottom: 0.5rem;\n }\n h2 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 32px;\n font-weight: 700;\n margin: 2rem 0 0.75rem;\n }\n h3 { font-size: 24px; font-weight: 600; }\n p { font-size: 16px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 12px; color: #6b7280; }\n </style>\n</head>\n<body>\n <h1>Good Typography</h1>\n <p>This page uses distinctive fonts, proper pairing, and strong hierarchy.</p>\n\n <h2>Two Font Families</h2>\n <p>Newsreader (serif) for headings, Karla for body. Clear contrast in both structure and personality, and neither face has been adopted into the current AI-default monoculture.</p>\n\n <h3>Strong Size Hierarchy</h3>\n <p>Sizes range from 12px to 48px — a 4:1 ratio with clear visual steps.</p>\n <p class=\"caption\">Caption text is clearly distinct from body.</p>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: 'Karla', system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n font-size: 16px;\n color: #111827;\n }\n h1 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 48px;\n font-weight: 700;\n margin-bottom: 0.5rem;\n }\n h2 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 32px;\n font-weight: 700;\n margin: 2rem 0 0.75rem;\n }\n h3 { font-size: 24px; font-weight: 600; }\n p { font-size: 16px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 12px; color: #6b7280; }\n ","classText":"caption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography Anti-Patterns — Side by Side</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Newsreader:opsz,wght@6..72,400;6..72,700&family=Karla:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 16px/1.5 system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col {\n min-width: 0;\n }\n\n .col-label {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .flag-type {\n font-family: 'Inter', sans-serif;\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .flag-type h1 {\n margin: 0 0 8px;\n font-size: 18px;\n line-height: 1.2;\n }\n\n .flag-type h2 {\n margin: 24px 0 8px;\n color: #4b5563;\n font-size: 16px;\n line-height: 1.25;\n }\n\n .flag-type h3 {\n margin: 18px 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .flag-type p {\n margin: 0 0 10px;\n color: #4b5563;\n font-size: 14px;\n }\n\n .flag-type .caption {\n color: #6b7280;\n font-size: 13px;\n }\n\n .flag-type .tight-leading-demo {\n line-height: 1.05;\n }\n\n .flag-type .tiny-body-demo {\n color: #374151;\n font-size: 10px;\n line-height: 1.6;\n }\n\n .flag-type .caps-body-demo {\n text-transform: uppercase;\n }\n\n .flag-type .tracked-body-demo {\n letter-spacing: 0.08em;\n }\n\n .flag-type .justified-body-demo {\n text-align: justify;\n }\n\n .pass-type {\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .pass-type h1,\n .pass-type h2 {\n font-family: 'Newsreader', Georgia, serif;\n }\n\n .pass-type h1 {\n margin: 0 0 10px;\n font-size: 48px;\n line-height: 0.98;\n font-weight: 700;\n }\n\n .pass-type h2 {\n margin: 32px 0 10px;\n font-size: 30px;\n line-height: 1.08;\n font-weight: 700;\n }\n\n .pass-type h3,\n .pass-type p,\n .pass-type .caption {\n font-family: 'Karla', system-ui, sans-serif;\n }\n\n .pass-type h3 {\n margin: 22px 0 6px;\n font-size: 22px;\n line-height: 1.15;\n }\n\n .pass-type p {\n margin: 0 0 12px;\n color: #374151;\n font-size: 16px;\n }\n\n .pass-type .caption {\n color: #4b5563;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n <article class=\"flag-type\">\n <h1>Typography Anti-Patterns</h1>\n <p>This side uses element-level typography failures that can be highlighted directly in the detector lab.</p>\n\n <h2>Tight Leading</h2>\n <p class=\"tight-leading-demo\">This paragraph has a line-height that is too tight for body copy. The lines press into each other and make scanning harder, especially once the text wraps across multiple lines in a production layout.</p>\n\n <h2>Tiny Body Text</h2>\n <p class=\"tiny-body-demo\">This body text is set at 10px, which is too small for real reading content and should be reserved for neither primary copy nor explanatory product text.</p>\n\n <h2>All-Caps Body</h2>\n <p class=\"caps-body-demo\">This entire paragraph is transformed to uppercase, which removes useful word shapes and makes longer body copy feel like a shout instead of a readable passage.</p>\n\n <h2>Wide Tracking</h2>\n <p class=\"tracked-body-demo\">This paragraph spreads every letter apart, a treatment that belongs on short labels at most, not on body copy that people need to read comfortably.</p>\n\n <h2>Justified Text</h2>\n <p class=\"justified-body-demo\">This paragraph uses justified alignment without automatic hyphenation. In narrow columns it creates uneven word spacing and distracting rivers through the text block.</p>\n\n <h2>Overused Font</h2>\n <p>Inter is also the dominant face on this side of the page, so the browser page-level detector has enough real text elements to show the global overused-font banner.</p>\n </article>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n <article class=\"pass-type\">\n <h1>Good Typography</h1>\n <p>This side keeps readable body defaults, a real font pairing, clear scale, and visible hierarchy.</p>\n\n <h2>Two Font Families</h2>\n <p>Newsreader carries display text, while Karla handles reading surfaces and supporting labels.</p>\n\n <h3>Strong Size Hierarchy</h3>\n <p>Sizes range from 12px to 48px with distinct roles for heading, subheading, body, and caption.</p>\n <p class=\"caption\">Caption text is intentionally smaller but still readable.</p>\n </article>\n </section>\n </div>\n\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 16px/1.5 system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col {\n min-width: 0;\n }\n\n .col-label {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .flag-type {\n font-family: 'Inter', sans-serif;\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .flag-type h1 {\n margin: 0 0 8px;\n font-size: 18px;\n line-height: 1.2;\n }\n\n .flag-type h2 {\n margin: 24px 0 8px;\n color: #4b5563;\n font-size: 16px;\n line-height: 1.25;\n }\n\n .flag-type h3 {\n margin: 18px 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .flag-type p {\n margin: 0 0 10px;\n color: #4b5563;\n font-size: 14px;\n }\n\n .flag-type .caption {\n color: #6b7280;\n font-size: 13px;\n }\n\n .flag-type .tight-leading-demo {\n line-height: 1.05;\n }\n\n .flag-type .tiny-body-demo {\n color: #374151;\n font-size: 10px;\n line-height: 1.6;\n }\n\n .flag-type .caps-body-demo {\n text-transform: uppercase;\n }\n\n .flag-type .tracked-body-demo {\n letter-spacing: 0.08em;\n }\n\n .flag-type .justified-body-demo {\n text-align: justify;\n }\n\n .pass-type {\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .pass-type h1,\n .pass-type h2 {\n font-family: 'Newsreader', Georgia, serif;\n }\n\n .pass-type h1 {\n margin: 0 0 10px;\n font-size: 48px;\n line-height: 0.98;\n font-weight: 700;\n }\n\n .pass-type h2 {\n margin: 32px 0 10px;\n font-size: 30px;\n line-height: 1.08;\n font-weight: 700;\n }\n\n .pass-type h3,\n .pass-type p,\n .pass-type .caption {\n font-family: 'Karla', system-ui, sans-serif;\n }\n\n .pass-type h3 {\n margin: 22px 0 6px;\n font-size: 22px;\n line-height: 1.15;\n }\n\n .pass-type p {\n margin: 0 0 12px;\n color: #374151;\n font-size: 16px;\n }\n\n .pass-type .caption {\n color: #4b5563;\n font-size: 12px;\n }\n ","classText":"grid\ncol\ncol-label\nflag-type\ntight-leading-demo\ntiny-body-demo\ncaps-body-demo\ntracked-body-demo\njustified-body-demo\ncol\ncol-label\npass-type\ncaption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>undersized-ui-text fixture</title>\n<style>\n /* Explicit pixel dimensions because jsdom does no layout. */\n body { font-family: system-ui, sans-serif; font-size: 16px; }\n .col { display: inline-block; width: 480px; vertical-align: top; }\n\n /* --- should-flag styles --- */\n .nav-link { font-size: 8px; }\n .category { font-size: 8px; }\n .meta { font-size: 9px; }\n .btn-small { font-size: 10px; width: 120px; height: 24px; }\n .cell-small { font-size: 9px; }\n .caps-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; }\n .footer-link { font-size: 8px; }\n\n /* --- should-pass styles --- */\n .legal { font-size: 10px; line-height: 1.5; } /* non-interactive footer smallprint at 10px */\n .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); font-size: 4px; }\n .sup-marker { font-size: 8px; }\n .sub-marker { font-size: 8px; }\n .em-parent { font-size: 20px; }\n .em-child { font-size: 0.6em; } /* resolves to 12px, above the floor */\n .mock-terminal { width: 400px; }\n .term-line { font-size: 9px; } /* legitimately small code/terminal text */\n .normal-link { font-size: 12px; } /* at the floor, allowed */\n</style>\n</head>\n<body>\n\n<!-- ================= SHOULD FLAG (functional/UI text below the 11px floor) ================= -->\n<div class=\"col\" id=\"should-flag\">\n <h2>Should flag</h2>\n\n <!-- interactive: nav link at 8px -->\n <nav aria-label=\"primary\">\n <a href=\"/docs\" class=\"nav-link\">Flag Nav Link</a>\n </nav>\n\n <!-- non-interactive furniture: category label at 8px -->\n <span class=\"category\">Flag Category</span>\n\n <!-- non-interactive furniture: meta row at 9px -->\n <span class=\"meta\">Flag Meta Row</span>\n\n <!-- interactive: button label at 10px -->\n <button type=\"button\" class=\"btn-small\">Flag Button</button>\n\n <!-- structural furniture: table cell at 9px -->\n <table>\n <tbody>\n <tr><td class=\"cell-small\">Flag Table Cell</td></tr>\n </tbody>\n </table>\n\n <!-- decorative letterspaced uppercase micro-label at 10px: STILL functional, not exempt -->\n <span class=\"caps-label\">Flag Caps Label</span>\n\n <!-- interactive text in a footer stays on the 11px floor: footer link at 8px -->\n <footer>\n <a href=\"/privacy\" class=\"footer-link\">Flag Footer Link</a>\n </footer>\n</div>\n\n<!-- ================= SHOULD PASS ================= -->\n<div class=\"col\" id=\"should-pass\">\n <h2>Should pass</h2>\n\n <!-- non-interactive legal fine print in a footer at 10px: floor drops to 10px for smallprint -->\n <footer>\n <p class=\"legal\">Pass Legal Fine Print copyright 2026 all rights reserved across this jurisdiction</p>\n </footer>\n\n <!-- visually-hidden / screen-reader text: never rendered, exempt -->\n <span class=\"sr-only\">Pass Sr Only</span>\n\n <!-- sup / sub markers are exempt by tag -->\n <p>Reference<sup class=\"sup-marker\">Pass Sup Marker</sup> and water<sub class=\"sub-marker\">Pass Sub Marker</sub></p>\n\n <!-- em-sized text relative to a large parent computes to 12px, above the floor -->\n <div class=\"em-parent\"><span class=\"em-child\">Pass Em Sized</span></div>\n\n <!-- code / terminal mock: legitimately small, exempt -->\n <div class=\"mock-terminal\">\n <span class=\"term-line\">Pass Terminal Line</span>\n </div>\n\n <!-- functional text exactly at the 12px floor is allowed -->\n <a href=\"/home\" class=\"normal-link\">Pass Normal Link</a>\n</div>\n\n</body>\n</html>\n",{"styleText":"\n /* Explicit pixel dimensions because jsdom does no layout. */\n body { font-family: system-ui, sans-serif; font-size: 16px; }\n .col { display: inline-block; width: 480px; vertical-align: top; }\n\n /* --- should-flag styles --- */\n .nav-link { font-size: 8px; }\n .category { font-size: 8px; }\n .meta { font-size: 9px; }\n .btn-small { font-size: 10px; width: 120px; height: 24px; }\n .cell-small { font-size: 9px; }\n .caps-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; }\n .footer-link { font-size: 8px; }\n\n /* --- should-pass styles --- */\n .legal { font-size: 10px; line-height: 1.5; } /* non-interactive footer smallprint at 10px */\n .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); font-size: 4px; }\n .sup-marker { font-size: 8px; }\n .sub-marker { font-size: 8px; }\n .em-parent { font-size: 20px; }\n .em-child { font-size: 0.6em; } /* resolves to 12px, above the floor */\n .mock-terminal { width: 400px; }\n .term-line { font-size: 9px; } /* legitimately small code/terminal text */\n .normal-link { font-size: 12px; } /* at the floor, allowed */\n","classText":"col\nnav-link\ncategory\nmeta\nbtn-small\ncell-small\ncaps-label\nfooter-link\ncol\nlegal\nsr-only\nsup-marker\nsub-marker\nem-parent\nem-child\nmock-terminal\nterm-line\nnormal-link"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Visual Contrast Fixture</title>\n <style>\n :root {\n --paper: #f7f3ee;\n --ink: #171717;\n --muted: #566174;\n }\n\n body {\n margin: 0;\n padding: 32px;\n background: var(--paper);\n color: var(--ink);\n font-family: system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n max-width: 980px;\n margin: 0 auto;\n }\n\n .column {\n display: grid;\n gap: 14px;\n }\n\n .column > h2 {\n margin: 0 0 2px;\n color: var(--muted);\n font-size: 13px;\n font-weight: 700;\n letter-spacing: 0.08em;\n line-height: 1.4;\n text-transform: uppercase;\n }\n\n .image-card {\n position: relative;\n display: grid;\n align-content: end;\n min-height: 180px;\n padding: 28px;\n border-radius: 8px;\n overflow: hidden;\n background-size: cover;\n background-position: center;\n }\n\n .image-card p {\n position: relative;\n z-index: 1;\n max-width: 28ch;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n line-height: 1.45;\n }\n\n .light-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23ece8df'/%3E%3Ccircle cx='308' cy='58' r='132' fill='%23faf7f0'/%3E%3Cpath d='M0 186c52-22 92-26 142-8s98 18 148-8 92-22 130 0v70H0z' fill='%23f5efe4'/%3E%3C/svg%3E\");\n }\n\n .pale-pattern {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23edf2f7'/%3E%3Cg fill='%23f8fafc'%3E%3Ccircle cx='60' cy='64' r='42'/%3E%3Ccircle cx='182' cy='142' r='58'/%3E%3Ccircle cx='336' cy='84' r='74'/%3E%3C/g%3E%3Cg stroke='%23dbe4ef' stroke-width='14' opacity='.55'%3E%3Cpath d='M-20 220C74 112 150 94 232 164s138 46 214-62' fill='none'/%3E%3C/g%3E%3C/svg%3E\");\n }\n\n .mist-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23b9c4cf'/%3E%3Cpath d='M0 52c74-28 138-22 194 18s124 38 226-28v198H0z' fill='%23cbd3dc'/%3E%3Ccircle cx='82' cy='176' r='68' fill='%23aeb8c4'/%3E%3C/svg%3E\");\n }\n\n .dark-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23191b20'/%3E%3Ccircle cx='316' cy='50' r='132' fill='%232d3440'/%3E%3Cpath d='M0 182c70-48 138-42 204-6s128 24 216-58v122H0z' fill='%23101318'/%3E%3C/svg%3E\");\n }\n\n .underlay {\n isolation: isolate;\n background: #1a1d24;\n }\n\n .underlay.light-shell {\n background: #f5f2ea;\n }\n\n .underlay img,\n .underlay svg {\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .underlay svg {\n display: block;\n }\n </style>\n</head>\n<body>\n <main class=\"grid\">\n <section class=\"column\" data-col=\"flag\">\n <h2>Should flag after pixel sampling</h2>\n\n <article class=\"image-card light-image\">\n <p style=\"color: rgb(255, 255, 255);\">White text on light image should be sampled by pixel contrast.</p>\n </article>\n\n <article class=\"image-card dark-image\">\n <p style=\"color: rgb(28, 30, 35);\">Dark text on dark image should be sampled by pixel contrast.</p>\n </article>\n\n <article class=\"image-card pale-pattern\">\n <p style=\"color: rgba(255, 255, 255, 0.72);\">Translucent white text on a pale pattern should use rendered pixels.</p>\n </article>\n\n <article class=\"image-card mist-image\">\n <p style=\"color: rgb(138, 146, 156);\">Muted gray text on a misty image should be sampled by pixel contrast.</p>\n </article>\n </section>\n\n <section class=\"column\" data-col=\"pass\">\n <h2>Should pass after pixel sampling</h2>\n\n <article class=\"image-card dark-image\">\n <p style=\"color: rgb(250, 247, 239);\">White text on dark image should pass the pixel contrast fallback.</p>\n </article>\n\n <article class=\"image-card light-image\">\n <p style=\"color: rgb(31, 35, 42);\">Dark text on light image should pass the pixel contrast fallback.</p>\n </article>\n\n <article class=\"image-card underlay\">\n <img alt=\"\" src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23141920'/%3E%3Ccircle cx='318' cy='70' r='126' fill='%23293442'/%3E%3C/svg%3E\">\n <p style=\"color: rgb(246, 242, 232);\">White text over a dark image underlay should pass.</p>\n </article>\n\n <article class=\"image-card underlay light-shell\">\n <svg viewBox=\"0 0 420 240\" aria-hidden=\"true\">\n <rect width=\"420\" height=\"240\" fill=\"#f3eee4\"></rect>\n <circle cx=\"86\" cy=\"70\" r=\"68\" fill=\"#fffaf2\"></circle>\n <path d=\"M0 176c72-24 132-22 190 6s128 30 230-34v92H0z\" fill=\"#e5dccd\"></path>\n </svg>\n <p style=\"color: rgb(35, 31, 27);\">Dark text over a light SVG underlay should pass.</p>\n </article>\n\n <article class=\"image-card light-image\" aria-hidden=\"true\">\n <p style=\"color: rgb(255, 255, 255);\">Hidden mockup text on a light image should not be sampled.</p>\n </article>\n </section>\n </main>\n</body>\n</html>\n",{"styleText":"\n :root {\n --paper: #f7f3ee;\n --ink: #171717;\n --muted: #566174;\n }\n\n body {\n margin: 0;\n padding: 32px;\n background: var(--paper);\n color: var(--ink);\n font-family: system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n max-width: 980px;\n margin: 0 auto;\n }\n\n .column {\n display: grid;\n gap: 14px;\n }\n\n .column > h2 {\n margin: 0 0 2px;\n color: var(--muted);\n font-size: 13px;\n font-weight: 700;\n letter-spacing: 0.08em;\n line-height: 1.4;\n text-transform: uppercase;\n }\n\n .image-card {\n position: relative;\n display: grid;\n align-content: end;\n min-height: 180px;\n padding: 28px;\n border-radius: 8px;\n overflow: hidden;\n background-size: cover;\n background-position: center;\n }\n\n .image-card p {\n position: relative;\n z-index: 1;\n max-width: 28ch;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n line-height: 1.45;\n }\n\n .light-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23ece8df'/%3E%3Ccircle cx='308' cy='58' r='132' fill='%23faf7f0'/%3E%3Cpath d='M0 186c52-22 92-26 142-8s98 18 148-8 92-22 130 0v70H0z' fill='%23f5efe4'/%3E%3C/svg%3E\");\n }\n\n .pale-pattern {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23edf2f7'/%3E%3Cg fill='%23f8fafc'%3E%3Ccircle cx='60' cy='64' r='42'/%3E%3Ccircle cx='182' cy='142' r='58'/%3E%3Ccircle cx='336' cy='84' r='74'/%3E%3C/g%3E%3Cg stroke='%23dbe4ef' stroke-width='14' opacity='.55'%3E%3Cpath d='M-20 220C74 112 150 94 232 164s138 46 214-62' fill='none'/%3E%3C/g%3E%3C/svg%3E\");\n }\n\n .mist-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23b9c4cf'/%3E%3Cpath d='M0 52c74-28 138-22 194 18s124 38 226-28v198H0z' fill='%23cbd3dc'/%3E%3Ccircle cx='82' cy='176' r='68' fill='%23aeb8c4'/%3E%3C/svg%3E\");\n }\n\n .dark-image {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 240'%3E%3Crect width='420' height='240' fill='%23191b20'/%3E%3Ccircle cx='316' cy='50' r='132' fill='%232d3440'/%3E%3Cpath d='M0 182c70-48 138-42 204-6s128 24 216-58v122H0z' fill='%23101318'/%3E%3C/svg%3E\");\n }\n\n .underlay {\n isolation: isolate;\n background: #1a1d24;\n }\n\n .underlay.light-shell {\n background: #f5f2ea;\n }\n\n .underlay img,\n .underlay svg {\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .underlay svg {\n display: block;\n }\n \nstyle=\"color: rgb(255, 255, 255);\"\nstyle=\"color: rgb(28, 30, 35);\"\nstyle=\"color: rgba(255, 255, 255, 0.72);\"\nstyle=\"color: rgb(138, 146, 156);\"\nstyle=\"color: rgb(250, 247, 239);\"\nstyle=\"color: rgb(31, 35, 42);\"\nstyle=\"color: rgb(246, 242, 232);\"\nstyle=\"color: rgb(35, 31, 27);\"\nstyle=\"color: rgb(255, 255, 255);\"","classText":"grid\ncolumn\nimage-card light-image\nimage-card dark-image\nimage-card pale-pattern\nimage-card mist-image\ncolumn\nimage-card dark-image\nimage-card light-image\nimage-card underlay\nimage-card underlay light-shell\nimage-card light-image"}],"result":[]}
{"args":["<!doctype html>\n<html><head>\n<!-- impeccable-disable low-contrast -- exported brand doc -->\n<style>\n .faint { color: #ff00aa; }\n .brand { font-family: Verdana } /* impeccable-disable-line design-system-font */\n .also { font-family: Verdana }\n</style></head>\n<body><p class=\"faint\">faint text</p><p class=\"brand\">x</p><p class=\"also\">y</p></body></html>\n",{"styleText":"\n .faint { color: #ff00aa; }\n .brand { font-family: Verdana } /* impeccable-disable-line design-system-font */\n .also { font-family: Verdana }\n","classText":"faint\nbrand\nalso"}],"result":[]}
{"args":["<!doctype html>\n<html><head><style>\n .card { font-family: Verdana, sans-serif; }\n .brand { font-family: Inter, sans-serif; }\n .dot { width: 8px; height: 8px; border-radius: 50%; animation: blink 1s infinite; }\n .off { color: #ff00aa; }\n</style></head>\n<body>\n <div class=\"card\">Hi</div>\n <p class=\"brand\">Brand</p>\n <span class=\"dot\"></span>\n <p class=\"off\">Off palette</p>\n</body></html>\n",{"styleText":"\n .card { font-family: Verdana, sans-serif; }\n .brand { font-family: Inter, sans-serif; }\n .dot { width: 8px; height: 8px; border-radius: 50%; animation: blink 1s infinite; }\n .off { color: #ff00aa; }\n","classText":"card\nbrand\ndot\noff"}],"result":[{"id":"pulsing-dot","snippet":".dot — 8x8px dot with infinite \"blink\" animation","selector":".dot"}]}
{"args":["<!doctype html>\n<html><head>\n<!-- impeccable-disable low-contrast -- exported brand doc -->\n<style>\n .faint { color: #ff00aa; }\n .brand { font-family: Verdana } /* impeccable-disable-line design-system-font */\n .also { font-family: Verdana }\n</style></head>\n<body><p class=\"faint\">faint text</p><p class=\"brand\">x</p><p class=\"also\">y</p></body></html>\n",{"styleText":"\n .faint { color: #ff00aa; }\n .brand { font-family: Verdana } /* impeccable-disable-line design-system-font */\n .also { font-family: Verdana }\n","classText":"faint\nbrand\nalso"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Anti-Patterns That Should Be Flagged</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 0.875rem; margin: 2.5rem 0 0.75rem; color: #4b5563; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.08em; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .cards { display: grid; gap: 1rem; max-width: 28rem; }\n .card { background: white; padding: 1rem; border-radius: 0.375rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .card h3 { font-weight: 600; color: #111827; font-size: 0.875rem; }\n .card p { font-size: 0.8125rem; color: #6b7280; margin-top: 0.25rem; }\n\n /* CSS side-tab variants */\n .card-shorthand-left { border-left: 4px solid #3b82f6; }\n .card-shorthand-right { border-right: 5px solid #8b5cf6; }\n .card-longhand-left { border-left-width: 3px; border-left-style: solid; border-left-color: #10b981; }\n .card-longhand-right { border-right-width: 6px; border-right-style: solid; border-right-color: #ef4444; }\n\n /* CSS top/bottom + border-radius */\n .card-css-top { border-radius: 12px; border-top: 4px solid #3b82f6; }\n .card-css-bottom { border-radius: 12px; border-bottom: 3px solid #8b5cf6; }\n </style>\n</head>\n<body>\n <h1>Anti-Patterns: Should Flag</h1>\n <p class=\"intro\">Every example on this page should be detected by the anti-pattern scanner.</p>\n\n <!-- TAILWIND SIDE-TAB -->\n <h2>Tailwind Side-Tab</h2>\n <div class=\"cards\">\n <div class=\"border-l-4 border-blue-500 bg-white p-4 rounded-r shadow-sm\">\n <h3>border-l-4 + rounded-r</h3>\n <p>The classic AI tell.</p>\n </div>\n <div class=\"border-r-4 border-purple-500 bg-white p-4 rounded-l shadow-sm\">\n <h3>border-r-4 + rounded-l</h3>\n <p>Right side variant.</p>\n </div>\n <div class=\"border-e-8 border-red-500 bg-white p-4 rounded-l shadow-sm\">\n <h3>border-e-8 + rounded-l</h3>\n <p>Logical inline-end, extra thick.</p>\n </div>\n </div>\n\n <!-- CSS SIDE-TAB -->\n <h2>CSS Side-Tab</h2>\n <div class=\"cards\">\n <div class=\"card card-shorthand-left\">\n <h3>border-left: 4px solid</h3>\n <p>CSS shorthand.</p>\n </div>\n <div class=\"card card-shorthand-right\">\n <h3>border-right: 5px solid</h3>\n <p>CSS shorthand right.</p>\n </div>\n <div class=\"card card-longhand-left\">\n <h3>border-left-width: 3px</h3>\n <p>CSS longhand.</p>\n </div>\n <div class=\"card card-longhand-right\">\n <h3>border-right-width: 6px</h3>\n <p>CSS longhand right.</p>\n </div>\n </div>\n\n <!-- TOP/BOTTOM + ROUNDED -->\n <h2>Top/Bottom + Rounded</h2>\n <div class=\"cards\">\n <div class=\"border-t-4 border-blue-500 rounded-lg bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-t-4 + rounded-lg</h3>\n <p class=\"text-sm text-gray-600\">Top accent on rounded card.</p>\n </div>\n <div class=\"border-b-4 border-purple-500 rounded-xl bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-b-4 + rounded-xl</h3>\n <p class=\"text-sm text-gray-600\">Bottom accent on rounded card.</p>\n </div>\n <div class=\"border-t-2 border-emerald-500 rounded-md bg-white p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm\">border-t-2 + rounded-md</h3>\n <p class=\"text-sm text-gray-600\">Even thin top border on rounded.</p>\n </div>\n <div class=\"card card-css-top\">\n <h3>CSS border-top + border-radius</h3>\n <p>Top border from style block.</p>\n </div>\n <div class=\"card card-css-bottom\">\n <h3>CSS border-bottom + border-radius</h3>\n <p>Bottom border from style block.</p>\n </div>\n </div>\n <!-- DARK MODE VARIANTS -->\n <h2>Dark Mode</h2>\n <div class=\"cards\">\n <div class=\"border-l-4 border-cyan-400 rounded-lg bg-gray-800 p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm text-white\">Dark card + border-l-4 + rounded</h3>\n <p class=\"text-sm text-gray-400\">Dark background doesn't make the side-tab OK.</p>\n </div>\n <div class=\"border-t-2 border-violet-500 rounded-xl bg-slate-900 p-4 shadow-sm\">\n <h3 class=\"font-semibold text-sm text-white\">Dark card + border-t-2 + rounded</h3>\n <p class=\"text-sm text-gray-400\">Top accent on dark rounded card.</p>\n </div>\n <div style=\"background: #1f2937; padding: 1rem; border-radius: 12px; border-left: 4px solid #f472b6; max-width: 28rem;\">\n <h3 style=\"font-weight: 600; font-size: 0.875rem; color: white;\">Dark CSS card + side border + radius</h3>\n <p style=\"font-size: 0.8125rem; color: #9ca3af;\">Inline dark card with side-tab.</p>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 2rem; margin-bottom: 0.5rem; }\n h2 { font-size: 0.875rem; margin: 2.5rem 0 0.75rem; color: #4b5563; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.08em; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .cards { display: grid; gap: 1rem; max-width: 28rem; }\n .card { background: white; padding: 1rem; border-radius: 0.375rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n .card h3 { font-weight: 600; color: #111827; font-size: 0.875rem; }\n .card p { font-size: 0.8125rem; color: #6b7280; margin-top: 0.25rem; }\n\n /* CSS side-tab variants */\n .card-shorthand-left { border-left: 4px solid #3b82f6; }\n .card-shorthand-right { border-right: 5px solid #8b5cf6; }\n .card-longhand-left { border-left-width: 3px; border-left-style: solid; border-left-color: #10b981; }\n .card-longhand-right { border-right-width: 6px; border-right-style: solid; border-right-color: #ef4444; }\n\n /* CSS top/bottom + border-radius */\n .card-css-top { border-radius: 12px; border-top: 4px solid #3b82f6; }\n .card-css-bottom { border-radius: 12px; border-bottom: 3px solid #8b5cf6; }\n \nstyle=\"background: #1f2937; padding: 1rem; border-radius: 12px; border-left: 4px solid #f472b6; max-width: 28rem;\"\nstyle=\"font-weight: 600; font-size: 0.875rem; color: white;\"\nstyle=\"font-size: 0.8125rem; color: #9ca3af;\"","classText":"intro\ncards\nborder-l-4 border-blue-500 bg-white p-4 rounded-r shadow-sm\nborder-r-4 border-purple-500 bg-white p-4 rounded-l shadow-sm\nborder-e-8 border-red-500 bg-white p-4 rounded-l shadow-sm\ncards\ncard card-shorthand-left\ncard card-shorthand-right\ncard card-longhand-left\ncard card-longhand-right\ncards\nborder-t-4 border-blue-500 rounded-lg bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\nborder-b-4 border-purple-500 rounded-xl bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\nborder-t-2 border-emerald-500 rounded-md bg-white p-4 shadow-sm\nfont-semibold text-sm\ntext-sm text-gray-600\ncard card-css-top\ncard card-css-bottom\ncards\nborder-l-4 border-cyan-400 rounded-lg bg-gray-800 p-4 shadow-sm\nfont-semibold text-sm text-white\ntext-sm text-gray-400\nborder-t-2 border-violet-500 rounded-xl bg-slate-900 p-4 shadow-sm\nfont-semibold text-sm text-white\ntext-sm text-gray-400"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Clean Patterns — Should NOT Flag</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .section { max-width: 28rem; }\n .card-rounded { background: white; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .card-rounded h3 { font-weight: 600; color: #111827; }\n .card-rounded p { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }\n .card-flat { background: white; padding: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .badge { display: inline-block; font-size: 0.75rem; padding: 0.125rem 0.5rem; border-radius: 9999px; background: #dbeafe; color: #1d4ed8; }\n\n /* Below-threshold side borders without radius */\n .thin-left { border-left: 1px solid #3b82f6; }\n .thin-right { border-right: 2px solid #8b5cf6; }\n </style>\n</head>\n<body>\n <h1>Clean Patterns: Should Pass</h1>\n <p class=\"intro\">None of these should be flagged. They're all intentional, well-established web patterns.</p>\n\n <!-- CLEAN CARDS -->\n <h2>Clean Cards</h2>\n <div class=\"section\">\n <div class=\"card-rounded\" style=\"border: 1px solid #e5e7eb;\">\n <h3>Full border card</h3>\n <p>Subtle 1px border all around. Clean and intentional.</p>\n </div>\n\n <div style=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-top: 1px solid #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n <h3 style=\"font-weight: 600; color: #111827;\">Top border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\">A hairline top rule without rounded corners is a clean section divider.</p>\n </div>\n\n <div style=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-bottom: 2px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n <h3 style=\"font-weight: 600; color: #111827;\">Bottom border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\">Bottom accent without rounded corners is also clean.</p>\n </div>\n\n <div class=\"card-rounded\">\n <span class=\"badge\">New</span>\n <h3>No border at all</h3>\n <p>Just a shadow. Simple and effective.</p>\n </div>\n </div>\n\n <!-- BELOW THRESHOLD -->\n <h2>Below Threshold (Thin, No Radius)</h2>\n <div class=\"section\">\n <div class=\"card-flat thin-left\">\n <p>border-left: 1px solid, no radius — not flagged</p>\n </div>\n <div class=\"card-flat thin-right\">\n <p>border-right: 2px solid, no radius — not flagged</p>\n </div>\n <div class=\"card-flat\" style=\"border-left: 1px solid #10b981;\">\n <p>1px inline border-left, no radius — not flagged</p>\n </div>\n </div>\n <!-- DARK MODE CLEAN -->\n <h2>Dark Mode (Clean)</h2>\n <div class=\"section\">\n <div style=\"background: #1f2937; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #374151; max-width: 28rem; margin-bottom: 1rem;\">\n <h3 style=\"font-weight: 600; color: white;\">Dark card, full border</h3>\n <p style=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\">Uniform 1px border all around. Clean.</p>\n </div>\n <div style=\"background: #111827; padding: 1.5rem; max-width: 28rem; margin-bottom: 1rem; border-bottom: 2px solid #6366f1;\">\n <h3 style=\"font-weight: 600; color: white;\">Dark section, bottom border, no radius</h3>\n <p style=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\">Bottom accent without radius is fine.</p>\n </div>\n <div style=\"background: #1e293b; padding: 1.5rem; border-radius: 0.5rem; max-width: 28rem; margin-bottom: 1rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3);\">\n <h3 style=\"font-weight: 600; color: white;\">Dark card, no border</h3>\n <p style=\"font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;\">Shadow only. Clean.</p>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }\n p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }\n .section { max-width: 28rem; }\n .card-rounded { background: white; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .card-rounded h3 { font-weight: 600; color: #111827; }\n .card-rounded p { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }\n .card-flat { background: white; padding: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }\n .badge { display: inline-block; font-size: 0.75rem; padding: 0.125rem 0.5rem; border-radius: 9999px; background: #dbeafe; color: #1d4ed8; }\n\n /* Below-threshold side borders without radius */\n .thin-left { border-left: 1px solid #3b82f6; }\n .thin-right { border-right: 2px solid #8b5cf6; }\n \nstyle=\"border: 1px solid #e5e7eb;\"\nstyle=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-top: 1px solid #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\"\nstyle=\"font-weight: 600; color: #111827;\"\nstyle=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\"\nstyle=\"max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-bottom: 2px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\"\nstyle=\"font-weight: 600; color: #111827;\"\nstyle=\"font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;\"\nstyle=\"border-left: 1px solid #10b981;\"\nstyle=\"background: #1f2937; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #374151; max-width: 28rem; margin-bottom: 1rem;\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\"\nstyle=\"background: #111827; padding: 1.5rem; max-width: 28rem; margin-bottom: 1rem; border-bottom: 2px solid #6366f1;\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;\"\nstyle=\"background: #1e293b; padding: 1.5rem; border-radius: 0.5rem; max-width: 28rem; margin-bottom: 1rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3);\"\nstyle=\"font-weight: 600; color: white;\"\nstyle=\"font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;\"","classText":"intro\nsection\ncard-rounded\ncard-rounded\nbadge\nsection\ncard-flat thin-left\ncard-flat thin-right\ncard-flat\nsection"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Border Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f8fafc;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1180px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n font-weight: 720;\n }\n\n .intro {\n max-width: 1180px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .case {\n margin: 0 0 14px;\n padding: 16px;\n background: #fdfdfd;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);\n }\n\n .case h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .case p {\n margin: 0;\n color: #475569;\n }\n\n .flag-left {\n border-left: 4px solid #2563eb;\n border-radius: 10px;\n }\n\n .flag-right {\n border-right: 5px solid #dc2626;\n border-radius: 10px;\n }\n\n .flag-left-plain {\n border-left: 4px solid #0f766e;\n }\n\n .flag-top {\n border-top: 4px solid #7c3aed;\n border-radius: 10px;\n }\n\n .flag-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 10px;\n }\n\n .flag-dark {\n background: #141820;\n color: #f4f7fb;\n border-left: 4px solid #38bdf8;\n border-radius: 10px;\n }\n\n .flag-dark p {\n color: #cbd5e1;\n }\n\n .pass-full-border {\n border: 1px solid #cbd5e1;\n border-radius: 10px;\n }\n\n .pass-neutral-side {\n border-left: 4px solid #d1d5db;\n border-radius: 10px;\n }\n\n .pass-thin-side {\n border-left: 1px solid #2563eb;\n border-radius: 10px;\n }\n\n .pass-status-alert {\n border-left: 4px solid #2563eb;\n }\n\n .flag-square-top {\n border-top: 4px solid #7c3aed;\n border-radius: 0;\n }\n\n .flag-square-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 0;\n }\n\n .pass-tab-underline {\n border-bottom: 3px solid #2563eb;\n border-radius: 0;\n }\n\n .pass-thick-band {\n border-top: 16px solid #7c3aed;\n border-radius: 0;\n }\n\n .pass-dark-uniform {\n background: #161a22;\n color: #f4f7fb;\n border: 1px solid #334155;\n border-radius: 10px;\n }\n\n .pass-dark-uniform p {\n color: #cbd5e1;\n }\n </style>\n</head>\n<body>\n <h1>Border side-tab detector cases</h1>\n <p class=\"intro\">A paired fixture for colored side borders and rounded accent borders. The left column should produce border findings; the right column should stay clean.</p>\n\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <div class=\"case flag-left\">\n <h3>Rounded card with colored left border</h3>\n <p>Classic side tab: border-left is thick, colored, and paired with rounded corners.</p>\n </div>\n\n <div class=\"case flag-right\">\n <h3>Rounded card with colored right border</h3>\n <p>The right-side version is the same visual trope mirrored.</p>\n </div>\n\n <div class=\"case flag-left-plain\">\n <h3>Thick colored side border without radius</h3>\n <p>A thick chromatic side stripe still reads as a tab even without rounded corners.</p>\n </div>\n\n <div class=\"case flag-top\">\n <h3>Rounded card with top accent border</h3>\n <p>A heavy top accent on a rounded card is the horizontal variant of the same pattern.</p>\n </div>\n\n <div class=\"case flag-bottom\">\n <h3>Rounded card with bottom accent border</h3>\n <p>The bottom accent is also a decorative stripe on a card surface.</p>\n </div>\n\n <div class=\"case flag-dark\">\n <h3>Dark card with bright side stripe</h3>\n <p>Dark surfaces can hide the cliche, but the thick colored side stripe remains.</p>\n </div>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <div class=\"case pass-full-border\">\n <h3>Uniform full border</h3>\n <p>A quiet all-around border frames the card instead of creating a side tab.</p>\n </div>\n\n <div class=\"case pass-neutral-side\">\n <h3>Neutral structural side rule</h3>\n <p>A gray structural rule can be a legitimate divider when it is not chromatic emphasis.</p>\n </div>\n\n <div class=\"case pass-thin-side\">\n <h3>Thin colored side rule</h3>\n <p>A 1px colored rule is below the decorative side-tab threshold.</p>\n </div>\n\n <div class=\"case pass-status-alert\" role=\"status\">\n <h3>Status toast with severity accent</h3>\n <p>A live status region carries a colored side bar as a severity accent, not a decorative side tab.</p>\n </div>\n\n <div class=\"case flag-square-top\">\n <h3>Square top border</h3>\n <p>A chromatic top stripe on a card is the horizontal variant of the side tab.</p>\n </div>\n\n <div class=\"case flag-square-bottom\">\n <h3>Square bottom border</h3>\n <p>A chromatic bottom stripe on a badge or card is the same decorative tell.</p>\n </div>\n\n <div class=\"case\" role=\"tablist\">\n <div class=\"case pass-tab-underline\" role=\"tab\" aria-selected=\"true\">\n <h3>Selected tab underline</h3>\n <p>An active-state underline inside a tablist is an affordance, not a stripe.</p>\n </div>\n </div>\n\n <div class=\"case pass-thick-band\">\n <h3>Thick color band</h3>\n <p>A band well past stripe thickness is a compositional block, not an accent stripe.</p>\n </div>\n\n <div class=\"case pass-dark-uniform\">\n <h3>Dark card with uniform border</h3>\n <p>The dark surface has a full neutral outline, not a chromatic side stripe.</p>\n </div>\n </section>\n </div>\n\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f8fafc;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1180px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n font-weight: 720;\n }\n\n .intro {\n max-width: 1180px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .case {\n margin: 0 0 14px;\n padding: 16px;\n background: #fdfdfd;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);\n }\n\n .case h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .case p {\n margin: 0;\n color: #475569;\n }\n\n .flag-left {\n border-left: 4px solid #2563eb;\n border-radius: 10px;\n }\n\n .flag-right {\n border-right: 5px solid #dc2626;\n border-radius: 10px;\n }\n\n .flag-left-plain {\n border-left: 4px solid #0f766e;\n }\n\n .flag-top {\n border-top: 4px solid #7c3aed;\n border-radius: 10px;\n }\n\n .flag-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 10px;\n }\n\n .flag-dark {\n background: #141820;\n color: #f4f7fb;\n border-left: 4px solid #38bdf8;\n border-radius: 10px;\n }\n\n .flag-dark p {\n color: #cbd5e1;\n }\n\n .pass-full-border {\n border: 1px solid #cbd5e1;\n border-radius: 10px;\n }\n\n .pass-neutral-side {\n border-left: 4px solid #d1d5db;\n border-radius: 10px;\n }\n\n .pass-thin-side {\n border-left: 1px solid #2563eb;\n border-radius: 10px;\n }\n\n .pass-status-alert {\n border-left: 4px solid #2563eb;\n }\n\n .flag-square-top {\n border-top: 4px solid #7c3aed;\n border-radius: 0;\n }\n\n .flag-square-bottom {\n border-bottom: 3px solid #ea580c;\n border-radius: 0;\n }\n\n .pass-tab-underline {\n border-bottom: 3px solid #2563eb;\n border-radius: 0;\n }\n\n .pass-thick-band {\n border-top: 16px solid #7c3aed;\n border-radius: 0;\n }\n\n .pass-dark-uniform {\n background: #161a22;\n color: #f4f7fb;\n border: 1px solid #334155;\n border-radius: 10px;\n }\n\n .pass-dark-uniform p {\n color: #cbd5e1;\n }\n ","classText":"intro\ngrid\ncol\ncase flag-left\ncase flag-right\ncase flag-left-plain\ncase flag-top\ncase flag-bottom\ncase flag-dark\ncol\ncase pass-full-border\ncase pass-neutral-side\ncase pass-thin-side\ncase pass-status-alert\ncase flag-square-top\ncase flag-square-bottom\ncase\ncase pass-tab-underline\ncase pass-thick-band\ncase pass-dark-uniform"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Anti-Patterns From Linked Stylesheet</title>\n <link rel=\"stylesheet\" href=\"external-styles.css\">\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1080px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n }\n\n p.intro {\n max-width: 1080px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1080px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .cards {\n display: grid;\n gap: 14px;\n }\n\n h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .cards p {\n margin: 0;\n color: #4b5563;\n }\n </style>\n</head>\n<body>\n <h1>Linked Stylesheet Anti-Patterns</h1>\n <p class=\"intro\">\n These elements get their anti-pattern styles from an external CSS file.\n Regex-only scanning misses these — only computed style analysis catches them.\n </p>\n\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"cards\">\n <div class=\"external-side-tab\">\n <h3>External side-tab class</h3>\n <p>border-left plus border-radius from a linked stylesheet.</p>\n </div>\n\n <div class=\"external-top-accent\">\n <h3>External top accent class</h3>\n <p>border-top plus border-radius from a linked stylesheet.</p>\n </div>\n </div>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"cards\">\n <div class=\"external-clean\">\n <h3>External clean card</h3>\n <p>Uniform 1px border from the linked stylesheet.</p>\n </div>\n\n <div class=\"external-neutral-side\">\n <h3>External neutral side rule</h3>\n <p>Thick side border, but the color is structural gray.</p>\n </div>\n\n <div class=\"external-square-top\">\n <h3>External square top rule</h3>\n <p>Heavy top border with no rounded card corners.</p>\n </div>\n </div>\n </section>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 14px/1.5 system-ui, sans-serif;\n }\n\n h1 {\n max-width: 1080px;\n margin: 0 auto 8px;\n font-size: 34px;\n line-height: 1.05;\n }\n\n p.intro {\n max-width: 1080px;\n margin: 0 auto 24px;\n color: #4b5563;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1080px;\n margin: 0 auto;\n }\n\n .col h2 {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .cards {\n display: grid;\n gap: 14px;\n }\n\n h3 {\n margin: 0 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .cards p {\n margin: 0;\n color: #4b5563;\n }\n \n/* External stylesheet that applies anti-pattern styles */\n\n/* Side-tab via external class */\n.external-side-tab {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-left: 4px solid #3b82f6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Top border accent via external class */\n.external-top-accent {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-top: 3px solid #8b5cf6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Overused font from external stylesheet */\n.external-inter {\n font-family: 'Inter', sans-serif;\n}\n\n/* Clean card — should NOT flag */\n.external-clean {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Neutral side rule — should NOT flag */\n.external-neutral-side {\n background: white;\n padding: 1rem;\n border-radius: 12px;\n border-left: 4px solid #d1d5db;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n/* Top rule without rounded card corners — should NOT flag */\n.external-square-top {\n background: white;\n padding: 1rem;\n border-radius: 0;\n border-top: 4px solid #8b5cf6;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n","classText":"intro\ngrid\ncol\ncards\nexternal-side-tab\nexternal-top-accent\ncol\ncards\nexternal-clean\nexternal-neutral-side\nexternal-square-top"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Color Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n /* Styled button color cases (declared via stylesheet so jsdom resolves them\n reliably — inline color doesn't always win over user-agent rules in jsdom\n for <a> and <button>). */\n .styled-pill-low { background-color: rgb(31, 26, 21); color: rgb(91, 79, 68); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .low-contrast-button { background-color: rgb(55, 65, 81); color: rgb(108, 114, 128); display: inline-block; padding: 10px 20px; border-radius: 8px; border: 0; font-size: 14px; }\n .good-pill-high { background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .inline-link-low { color: rgb(170, 170, 170); }\n /* Chip / badge cases: text-bearing spans painting their own background. */\n .chip-sev-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(92, 84, 73); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-ok-high { display: inline-block; background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-sub9-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(150, 60, 55); font-size: 8px; padding: 1px 4px; }\n .panel-reset { background: rgb(28, 30, 38); color: rgb(230, 232, 237); padding: 12px; }\n .panel-reset code { background: rgb(246, 242, 244); border-radius: 3px; padding: 1px 4px; }\n .panel-reset pre code { background: none; }\n /* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill\n (text-fill-color: transparent), not a backdrop. The inherited `color`\n (#e8e6e3) is never painted, so measuring it against the element's own\n gradient stops (#6d8cff / #a78bfa) is a false positive. */\n .ox-grad-text { background: linear-gradient(135deg, #6d8cff 0%, #a78bfa 50%, #6d8cff 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: #e8e6e3; font-size: 40px; font-weight: 800; }\n /* issue #409 Case B: a 9%-alpha accent glow stop over a dark surface. The\n stop composites to ~#121f1f, not the full-opacity #34c0a8, so text stays\n high-contrast. The dark wrapper supplies the surface beneath the glow. */\n .ox-dark-wrap { background: #0f0f11; padding: 16px; }\n .ox-glow { background: linear-gradient(160deg, rgba(52,192,168,0.09) 0%, #141419 65%); padding: 20px; }\n .ox-glow p { color: #e8e6e3; font-size: 18px; }\n .ox-glow .muted { color: #8e8c89; font-size: 16px; }\n /* Gradient stop written as color-mix with a bright nested ingredient.\n The stop paints as a 16% wash of the light oklch color (composited\n over the dark wrap it is near-black); the nested oklch(90% ...) is an\n INGREDIENT, never painted. A parser that re-extracts nested tokens\n appends it as a phantom opaque light stop and the worst-case ratio\n then flags the light text at ~1:1 against a color nobody sees. */\n .mix-dark-wrap { background: #0f0f11; padding: 16px; }\n .mix-glow { background: linear-gradient(160deg, color-mix(in oklab, oklch(90% 0.02 95) 16%, transparent) 0%, #141419 65%); padding: 20px; }\n .mix-glow p { color: #ded9cf; font-size: 16px; }\n /* currentcolor surface: background-color paints with the element's own\n text color, which is itself a var() token here. jsdom hands both\n through verbatim, so the walk must resolve the token via the\n custom-prop map instead of abstaining on a knowable surface. */\n :root { --fixture-bone: #e8e2d6; }\n .currentcolor-surface { background-color: currentcolor; color: var(--fixture-bone); padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .currentcolor-low-text { color: #cfc9bd; font-size: 14px; }\n .currentcolor-good-text { color: #3a352c; font-size: 14px; }\n /* Same shape but the token does not exist: the surface truly cannot be\n read, so the walk must abstain rather than guess. */\n .currentcolor-unknown { background-color: currentcolor; color: var(--fixture-undefined-token); padding: 14px 16px; border-radius: 10px; }\n .currentcolor-unknown p { color: #efe9dd; font-size: 14px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Pure black surfaces</h3>\n <div class=\"card\" style=\"background: #000000; color: white;\">\n <p>Pure #000 background</p>\n </div>\n\n <h3>Gray on color</h3>\n <div class=\"card\" style=\"background: rgb(59, 130, 246);\">\n <p style=\"color: rgb(150, 150, 150);\">Gray text on blue background</p>\n </div>\n <div class=\"card\" style=\"background: rgb(16, 185, 129);\">\n <p style=\"color: rgb(180, 180, 180);\">Gray text on green background</p>\n </div>\n\n <h3>Low contrast</h3>\n <div class=\"card\" style=\"background: white;\">\n <p style=\"color: rgb(200, 200, 200);\">Light gray text on white — very low contrast</p>\n </div>\n <div class=\"card\" style=\"background: rgb(30, 30, 30);\">\n <p style=\"color: rgb(80, 80, 80);\">Dark gray text on near-black — low contrast</p>\n </div>\n <!-- Gradient background — gray heading is unreadable across every stop -->\n <div class=\"card\" style=\"background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;\">\n <h1 style=\"color: #808080; font-size: 32px; font-weight: 800; margin: 0;\">Welcome to Our Platform</h1>\n <p style=\"color: #666666; font-size: 14px;\">The best solution for modern teams and businesses</p>\n </div>\n\n <h3>Gradient text</h3>\n <h3 style=\"background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;\">\n Gradient Heading\n </h3>\n\n <h3>AI color palette</h3>\n <h1 style=\"color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;\">Purple heading text</h1>\n\n <h3>Styled &lt;a&gt; and &lt;button&gt; with low contrast</h3>\n <!-- A pill-style <a> with its own opaque background and low-contrast text.\n Currently SAFE_TAGS skips <a> entirely; this case asserts the contrast\n check still runs when the element is styled as a button. Mirrors a real\n bug from the landing-demo: warm-charcoal text on near-black bg. -->\n <a href=\"#\" class=\"styled-pill-low\" data-test=\"styled-pill\">Get started</a>\n <!-- A <button> element with its own opaque background and low-contrast text.\n Same SAFE_TAGS exception — buttons styled with their own background should\n still pass through the contrast check. -->\n <button class=\"low-contrast-button\" data-test=\"low-contrast-button\">Submit</button>\n\n <h3>Text-bearing chip with low contrast</h3>\n <!-- A <span> chip painting its own opaque background under direct text.\n SAFE_TAGS used to skip spans categorically; the styled-control\n exception must cover any text-bearing chip or badge. Mirrors a real\n bug: a SEV-2 chip whose white text lost a specificity fight and\n rendered muted brown on red at 1.2:1. -->\n <span class=\"chip-sev-low\" data-test=\"chip-sev\">SEV-2</span>\n\n <h3>Tailwind color anti-patterns</h3>\n <div class=\"bg-black text-white p-4 rounded card\" style=\"background: black; color: white;\">\n <p>bg-black — pure black bg</p>\n </div>\n <div class=\"bg-blue-500 text-gray-400 p-4 rounded card\" style=\"background: rgb(59, 130, 246);\">\n <p style=\"color: rgb(156, 163, 175);\">text-gray-400 on bg-blue-500 — gray on color</p>\n </div>\n <h3 class=\"text-purple-500 text-2xl font-bold\" style=\"color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;\">text-purple-500 heading</h3>\n <div class=\"bg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card\" style=\"background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;\">\n <p>Purple-to-indigo gradient</p>\n </div>\n\n <h3>currentcolor surface via var() token</h3>\n <!-- background-color: currentcolor with color: var(--fixture-bone).\n The surface is knowable (bone #e8e2d6), so the faint text on it is\n a real low-contrast finding, not an abstention. -->\n <div class=\"currentcolor-surface\" data-test=\"currentcolor-low\">\n <p class=\"currentcolor-low-text\">Faint warm gray on a bone currentcolor surface</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Tinted neutrals</h3>\n <div class=\"card\" style=\"background: rgb(26, 26, 30);\">\n <p style=\"color: rgb(240, 240, 245);\">Near-black bg, near-white text — tinted, not pure</p>\n </div>\n <div class=\"card\" style=\"background: rgb(250, 250, 252);\">\n <p style=\"color: rgb(20, 20, 25);\">Near-white bg, near-black text — good contrast, tinted</p>\n </div>\n\n <h3>Pure white surface is acceptable</h3>\n <div class=\"card\" style=\"background: #ffffff;\">\n <p style=\"color: #111827;\">Pure #fff background with dark text — acceptable contrast</p>\n </div>\n\n <h3>Good contrast</h3>\n <div class=\"card\" style=\"background: rgb(30, 64, 175);\">\n <p style=\"color: rgb(240, 240, 245);\">Near-white text on dark blue — high contrast, not pure white</p>\n </div>\n <div class=\"card\" style=\"background: rgb(16, 185, 129);\">\n <p style=\"color: rgb(5, 46, 32);\">Dark green text on green bg — same hue family</p>\n </div>\n\n <h3>Distinctive accents (not AI purple)</h3>\n <h3 style=\"color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;\">Red heading — not AI purple</h3>\n <h3 style=\"color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;\">Amber heading — distinctive</h3>\n\n <h3>Background-image url() ancestor (not low contrast)</h3>\n <!-- White text on transparent bg where ancestor has a background-image.\n The detector cannot determine the image color, so it must NOT assume\n white and report a false low-contrast finding. -->\n <div data-test=\"bg-image-ancestor\" style=\"background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;\">\n <p style=\"color: rgb(255, 255, 255); font-size: 16px;\">White text on image background — should not flag low-contrast</p>\n </div>\n\n <h3>Tailwind opacity-modified bg-black (not pure black)</h3>\n <!-- bg-black/N classes are NOT pure black — they apply an alpha modifier.\n The detector must not match these as pure-black-white. -->\n <div class=\"bg-black/3 p-4 rounded card\" data-test=\"bg-black-opacity\" style=\"background: rgba(0,0,0,0.03);\">\n <p>bg-black/3 — 3% opacity, not pure black</p>\n </div>\n <div class=\"hover:bg-black/5 p-4 rounded card\" data-test=\"bg-black-hover-opacity\" style=\"background: rgba(0,0,0,0);\">\n <p>hover:bg-black/5 — hover state, not pure black</p>\n </div>\n <div class=\"bg-black/50 p-4 rounded card\" data-test=\"bg-black-half-opacity\" style=\"background: rgba(0,0,0,0.5);\">\n <p style=\"color: white;\">bg-black/50 — 50% opacity, not pure black</p>\n </div>\n\n <h3>Inline &lt;a&gt; without own background (must remain skipped)</h3>\n <!-- A regular text link inside a paragraph. No own background. SAFE_TAGS\n must continue to skip these — flagging them would create noise on\n every text link on the web. The detector exception for styled\n buttons must not regress to flagging plain inline links. -->\n <p>Read more about this <a href=\"#\" class=\"inline-link-low\" data-test=\"inline-link\">here</a> if you want to learn more about the topic.</p>\n\n <h3>Styled &lt;a&gt; with good contrast (must not flag)</h3>\n <!-- A pill-style <a> styled correctly: cream text on near-black bg,\n high contrast. The detector exception for styled buttons must\n let the contrast check run, but this case must clearly pass. -->\n <a href=\"#\" class=\"good-pill-high\" data-test=\"good-pill\">High contrast pill</a>\n\n <h3>Chip with good contrast (must not flag)</h3>\n <span class=\"chip-ok-high\" data-test=\"chip-ok\">HEALTHY</span>\n\n <h3>Sub-9px decorative chip (below the font floor, must not flag)</h3>\n <span class=\"chip-sub9-low\" data-test=\"chip-sub9\">tick</span>\n\n <h3>background: none resets an earlier background-color</h3>\n <!-- The `background` shorthand resets background-color. `pre code\n { background: none }` after `code { background: <light> }` leaves\n the code text transparent over the dark panel; the cascade must not\n keep measuring it against the light surface it never paints. -->\n <div class=\"panel-reset\" data-test=\"panel-reset\">\n <pre><code data-test=\"code-reset\">light text over the dark panel, not the light code surface</code></pre>\n </div>\n\n <h3>Gradient-clipped text (issue #409 Case A — must not flag contrast)</h3>\n <!-- background-clip: text with a transparent fill: the gradient paints the\n glyphs, not a surface behind them. The inherited light `color` is\n never painted. The gradient-text pattern still flags; the backdrop\n contrast rules (low-contrast / gray-on-color) must not. -->\n <h1 class=\"ox-grad-text\" data-test=\"ox-grad-text\">Gradient Clipped Heading Text</h1>\n\n <h3>Faint accent-glow gradient (issue #409 Case B — must not flag contrast)</h3>\n <!-- A 9%-alpha teal glow over a dark section. Composited against the dark\n surface the stop is near-black, so the light and gray text on it are\n high-contrast. Treating the stop as opaque #34c0a8 was the false\n positive that flagged every text child. -->\n <div class=\"ox-dark-wrap\">\n <div class=\"ox-glow\" data-test=\"ox-glow\">\n <p>Light body copy on a faint accent glow that composites to near-black</p>\n <p class=\"muted\">Muted secondary line on the same faint glow area here</p>\n </div>\n </div>\n\n <h3>color-mix gradient stop: nested ingredient is not a surface</h3>\n <div class=\"mix-dark-wrap\">\n <div class=\"mix-glow\" data-test=\"mix-glow\">\n <p>Light copy on a faint mixed wash over a dark ground stays readable</p>\n </div>\n </div>\n\n <h3>currentcolor surface with good contrast</h3>\n <div class=\"currentcolor-surface\" data-test=\"currentcolor-good\">\n <p class=\"currentcolor-good-text\">Dark ink text on a bone currentcolor surface</p>\n </div>\n\n <h3>currentcolor surface with unresolvable token (must abstain)</h3>\n <!-- The var() token is undefined, so the surface genuinely cannot be\n read. The walk must abstain instead of guessing a background for\n the light text inside. -->\n <div class=\"currentcolor-unknown\" data-test=\"currentcolor-unknown\">\n <p>Light text on an unknowable currentcolor surface</p>\n </div>\n\n <h3>Emoji on light backgrounds</h3>\n <!-- Emojis render as multicolor glyphs regardless of CSS color, so the\n CSS color is irrelevant for contrast. These should NOT be flagged. -->\n <div class=\"card emoji-test\" data-test=\"emoji-light\" style=\"background: #ffe4e6;\">\n <p style=\"color: #ffe4e6; font-size: 24px;\">⚠️ 🚨 ✨ 🎨</p>\n </div>\n <div class=\"card emoji-test\" data-test=\"emoji-dark\" style=\"background: #1a1a1a;\">\n <p style=\"color: #1a1a1a; font-size: 24px;\">⚠️ 🚨 ✨ 🎨</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n /* Styled button color cases (declared via stylesheet so jsdom resolves them\n reliably — inline color doesn't always win over user-agent rules in jsdom\n for <a> and <button>). */\n .styled-pill-low { background-color: rgb(31, 26, 21); color: rgb(91, 79, 68); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .low-contrast-button { background-color: rgb(55, 65, 81); color: rgb(108, 114, 128); display: inline-block; padding: 10px 20px; border-radius: 8px; border: 0; font-size: 14px; }\n .good-pill-high { background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }\n .inline-link-low { color: rgb(170, 170, 170); }\n /* Chip / badge cases: text-bearing spans painting their own background. */\n .chip-sev-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(92, 84, 73); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-ok-high { display: inline-block; background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 2px; }\n .chip-sub9-low { display: inline-block; background-color: rgb(182, 50, 45); color: rgb(150, 60, 55); font-size: 8px; padding: 1px 4px; }\n .panel-reset { background: rgb(28, 30, 38); color: rgb(230, 232, 237); padding: 12px; }\n .panel-reset code { background: rgb(246, 242, 244); border-radius: 3px; padding: 1px 4px; }\n .panel-reset pre code { background: none; }\n /* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill\n (text-fill-color: transparent), not a backdrop. The inherited `color`\n (#e8e6e3) is never painted, so measuring it against the element's own\n gradient stops (#6d8cff / #a78bfa) is a false positive. */\n .ox-grad-text { background: linear-gradient(135deg, #6d8cff 0%, #a78bfa 50%, #6d8cff 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: #e8e6e3; font-size: 40px; font-weight: 800; }\n /* issue #409 Case B: a 9%-alpha accent glow stop over a dark surface. The\n stop composites to ~#121f1f, not the full-opacity #34c0a8, so text stays\n high-contrast. The dark wrapper supplies the surface beneath the glow. */\n .ox-dark-wrap { background: #0f0f11; padding: 16px; }\n .ox-glow { background: linear-gradient(160deg, rgba(52,192,168,0.09) 0%, #141419 65%); padding: 20px; }\n .ox-glow p { color: #e8e6e3; font-size: 18px; }\n .ox-glow .muted { color: #8e8c89; font-size: 16px; }\n /* Gradient stop written as color-mix with a bright nested ingredient.\n The stop paints as a 16% wash of the light oklch color (composited\n over the dark wrap it is near-black); the nested oklch(90% ...) is an\n INGREDIENT, never painted. A parser that re-extracts nested tokens\n appends it as a phantom opaque light stop and the worst-case ratio\n then flags the light text at ~1:1 against a color nobody sees. */\n .mix-dark-wrap { background: #0f0f11; padding: 16px; }\n .mix-glow { background: linear-gradient(160deg, color-mix(in oklab, oklch(90% 0.02 95) 16%, transparent) 0%, #141419 65%); padding: 20px; }\n .mix-glow p { color: #ded9cf; font-size: 16px; }\n /* currentcolor surface: background-color paints with the element's own\n text color, which is itself a var() token here. jsdom hands both\n through verbatim, so the walk must resolve the token via the\n custom-prop map instead of abstaining on a knowable surface. */\n :root { --fixture-bone: #e8e2d6; }\n .currentcolor-surface { background-color: currentcolor; color: var(--fixture-bone); padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .currentcolor-low-text { color: #cfc9bd; font-size: 14px; }\n .currentcolor-good-text { color: #3a352c; font-size: 14px; }\n /* Same shape but the token does not exist: the surface truly cannot be\n read, so the walk must abstain rather than guess. */\n .currentcolor-unknown { background-color: currentcolor; color: var(--fixture-undefined-token); padding: 14px 16px; border-radius: 10px; }\n .currentcolor-unknown p { color: #efe9dd; font-size: 14px; }\n \nstyle=\"background: #000000; color: white;\"\nstyle=\"background: rgb(59, 130, 246);\"\nstyle=\"color: rgb(150, 150, 150);\"\nstyle=\"background: rgb(16, 185, 129);\"\nstyle=\"color: rgb(180, 180, 180);\"\nstyle=\"background: white;\"\nstyle=\"color: rgb(200, 200, 200);\"\nstyle=\"background: rgb(30, 30, 30);\"\nstyle=\"color: rgb(80, 80, 80);\"\nstyle=\"background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;\"\nstyle=\"color: #808080; font-size: 32px; font-weight: 800; margin: 0;\"\nstyle=\"color: #666666; font-size: 14px;\"\nstyle=\"background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;\"\nstyle=\"color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;\"\nstyle=\"background: black; color: white;\"\nstyle=\"background: rgb(59, 130, 246);\"\nstyle=\"color: rgb(156, 163, 175);\"\nstyle=\"color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;\"\nstyle=\"background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;\"\nstyle=\"background: rgb(26, 26, 30);\"\nstyle=\"color: rgb(240, 240, 245);\"\nstyle=\"background: rgb(250, 250, 252);\"\nstyle=\"color: rgb(20, 20, 25);\"\nstyle=\"background: #ffffff;\"\nstyle=\"color: #111827;\"\nstyle=\"background: rgb(30, 64, 175);\"\nstyle=\"color: rgb(240, 240, 245);\"\nstyle=\"background: rgb(16, 185, 129);\"\nstyle=\"color: rgb(5, 46, 32);\"\nstyle=\"color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;\"\nstyle=\"color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;\"\nstyle=\"background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;\"\nstyle=\"color: rgb(255, 255, 255); font-size: 16px;\"\nstyle=\"background: rgba(0,0,0,0.03);\"\nstyle=\"background: rgba(0,0,0,0);\"\nstyle=\"background: rgba(0,0,0,0.5);\"\nstyle=\"color: white;\"\nstyle=\"background: #ffe4e6;\"\nstyle=\"color: #ffe4e6; font-size: 24px;\"\nstyle=\"background: #1a1a1a;\"\nstyle=\"color: #1a1a1a; font-size: 24px;\"","classText":"grid\ncol\ncard\ncard\ncard\ncard\ncard\ncard\nstyled-pill-low\nlow-contrast-button\nchip-sev-low\nbg-black text-white p-4 rounded card\nbg-blue-500 text-gray-400 p-4 rounded card\ntext-purple-500 text-2xl font-bold\nbg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card\ncurrentcolor-surface\ncurrentcolor-low-text\ncol\ncard\ncard\ncard\ncard\ncard\nbg-black/3 p-4 rounded card\nhover:bg-black/5 p-4 rounded card\nbg-black/50 p-4 rounded card\ninline-link-low\ngood-pill-high\nchip-ok-high\nchip-sub9-low\npanel-reset\nox-grad-text\nox-dark-wrap\nox-glow\nmuted\nmix-dark-wrap\nmix-glow\ncurrentcolor-surface\ncurrentcolor-good-text\ncurrentcolor-unknown\ncard emoji-test\ncard emoji-test"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":".currentcolor-unknown p"},{"id":"gradient-text","snippet":"background-clip: text + gradient","selector":"/* issue #409 Case A: gradient-clipped text. The gradient IS the glyph fill (text-fill-color: transparent), not a backdrop. The inherited `color` (#e8e6e3) is never painted, so measuring it against the element's own gradient stops (#6d8cff / #a78bfa) is a false positive. */ .ox-grad-text"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Fixture: data-impeccable-ignore scoped waivers</title>\n<style>\n body { margin: 0; padding: 40px; background: #faf8f4; font-family: Arial, sans-serif; color: #262421; }\n /* Every case carries a UNIQUE border-left width so each finding's snippet\n (`border-left: Npx`) attributes to exactly one case. Flag widths: 5, 6,\n 8, 12. Pass widths: 4, 7, 9, 10, 11. */\n .tabbed {\n width: 420px;\n padding: 16px 20px;\n margin: 0 0 24px;\n background: #ffffff;\n border: 1px solid #ddd8d0;\n border-left: 6px solid #b8422e;\n }\n .w4 { border-left-width: 4px; }\n .w5 { border-left-width: 5px; }\n .w7 { border-left-width: 7px; }\n .w8 { border-left-width: 8px; }\n .w9 { border-left-width: 9px; }\n .w10 { border-left-width: 10px; }\n .w11 { border-left-width: 11px; }\n .w12 { border-left-width: 12px; }\n /* Marquee specimen: keyframes + an infinite loop track. The section that\n hosts it carries a marquee waiver; the CSS-scan finding must resolve the\n selector to the live element and honor the ancestor attribute. */\n @keyframes ticker-loop { from { transform: translateX(0); } to { transform: translateX(-50%); } }\n .quote-track { display: flex; width: max-content; animation: ticker-loop 80s linear infinite; }\n .quote-track span { padding: 8px 16px; }\n /* Dead grid CSS: this two-axis hairline grid selector matches nothing on\n this page, so the browser pass must drop the codex-grid finding — the\n pattern never renders here. */\n .never-rendered-grid {\n background:\n linear-gradient(90deg, rgba(200,180,120,0.08) 1px, transparent 1px),\n linear-gradient(180deg, rgba(200,180,120,0.08) 1px, transparent 1px);\n background-size: 24px 24px;\n }\n /* Overshoot bezier inside a keyframe STEP: enclosingCssSelector must refuse\n `to` as a selector (it would match nothing and wrongly drop the finding),\n so this bounce-easing finding stays page-level and is retained. */\n @keyframes springy {\n from { transform: none; }\n to { transform: translateY(-4px); animation-timing-function: cubic-bezier(0.3, 1.8, 0.6, 1); }\n }\n</style>\n</head>\n<body>\n <!-- FLAG 6px — control: no waiver anywhere above. -->\n <article class=\"tabbed control-flags\">The control case has a 6px side-tab accent border and no waiver, so the side-tab rule must flag it.</article>\n\n <!-- FLAG 8px — waiver names a different rule, so side-tab still applies. -->\n <section data-impeccable-ignore=\"pulsing-dot\">\n <article class=\"tabbed w8 flags-other-rule-waived\">The waiver names pulsing-dot, not side-tab, so this 8px case must still flag.</article>\n </section>\n\n <!-- FLAG 12px — waiver sits on a SIBLING, not an ancestor: no effect. -->\n <div data-impeccable-ignore=\"side-tab\"></div>\n <article class=\"tabbed w12 flags-sibling-waiver\">A waiver on a preceding sibling is not an ancestor; this 12px case must still flag.</article>\n\n <!-- FLAG 5px — waiver value misspells the rule id: exact matching only. -->\n <section data-impeccable-ignore=\"side-tabs\">\n <article class=\"tabbed w5 flags-misspelled-rule\">\"side-tabs\" is not a rule id; the 5px case must still flag.</article>\n </section>\n\n <!-- PASS 7px — exact-rule waiver on the container: the subtree is exempt. -->\n <section data-impeccable-ignore=\"side-tab\">\n <article class=\"tabbed w7 pass-exact-rule\">Inside a container waived for exactly side-tab; must not flag.</article>\n </section>\n\n <!-- PASS 9px — two levels below the waiving container. -->\n <section data-impeccable-ignore=\"side-tab\">\n <div>\n <article class=\"tabbed w9 pass-nested-depth\">Two levels below the waiving container; the ancestor walk must still find it.</article>\n </div>\n </section>\n\n <!-- PASS 10px — star waiver: every rule is exempt in the subtree. -->\n <section data-impeccable-ignore=\"*\">\n <article class=\"tabbed w10 pass-star\">Inside a star-waived container; must not flag.</article>\n </section>\n\n <!-- PASS 11px — comma list including side-tab. -->\n <section data-impeccable-ignore=\"pulsing-dot, side-tab\">\n <article class=\"tabbed w11 pass-rule-list\">side-tab appears in the comma list; must not flag.</article>\n </section>\n\n <!-- PASS 4px — waiver on the flagged element itself (self counts). -->\n <article class=\"tabbed w4 pass-self-waiver\" data-impeccable-ignore=\"side-tab\">The waiver sits on the element itself; must not flag.</article>\n\n <!-- Live marquee inside a rule-scoped waiver: the CSS-scan marquee finding\n resolves .quote-track to this element and is waived by the ancestor. -->\n <section data-impeccable-ignore=\"marquee\">\n <div class=\"quote-track\"><span>One voice.</span><span>Another voice.</span><span>One voice.</span><span>Another voice.</span></div>\n </section>\n</body>\n</html>\n",{"styleText":"\n body { margin: 0; padding: 40px; background: #faf8f4; font-family: Arial, sans-serif; color: #262421; }\n /* Every case carries a UNIQUE border-left width so each finding's snippet\n (`border-left: Npx`) attributes to exactly one case. Flag widths: 5, 6,\n 8, 12. Pass widths: 4, 7, 9, 10, 11. */\n .tabbed {\n width: 420px;\n padding: 16px 20px;\n margin: 0 0 24px;\n background: #ffffff;\n border: 1px solid #ddd8d0;\n border-left: 6px solid #b8422e;\n }\n .w4 { border-left-width: 4px; }\n .w5 { border-left-width: 5px; }\n .w7 { border-left-width: 7px; }\n .w8 { border-left-width: 8px; }\n .w9 { border-left-width: 9px; }\n .w10 { border-left-width: 10px; }\n .w11 { border-left-width: 11px; }\n .w12 { border-left-width: 12px; }\n /* Marquee specimen: keyframes + an infinite loop track. The section that\n hosts it carries a marquee waiver; the CSS-scan finding must resolve the\n selector to the live element and honor the ancestor attribute. */\n @keyframes ticker-loop { from { transform: translateX(0); } to { transform: translateX(-50%); } }\n .quote-track { display: flex; width: max-content; animation: ticker-loop 80s linear infinite; }\n .quote-track span { padding: 8px 16px; }\n /* Dead grid CSS: this two-axis hairline grid selector matches nothing on\n this page, so the browser pass must drop the codex-grid finding — the\n pattern never renders here. */\n .never-rendered-grid {\n background:\n linear-gradient(90deg, rgba(200,180,120,0.08) 1px, transparent 1px),\n linear-gradient(180deg, rgba(200,180,120,0.08) 1px, transparent 1px);\n background-size: 24px 24px;\n }\n /* Overshoot bezier inside a keyframe STEP: enclosingCssSelector must refuse\n `to` as a selector (it would match nothing and wrongly drop the finding),\n so this bounce-easing finding stays page-level and is retained. */\n @keyframes springy {\n from { transform: none; }\n to { transform: translateY(-4px); animation-timing-function: cubic-bezier(0.3, 1.8, 0.6, 1); }\n }\n","classText":"tabbed control-flags\ntabbed w8 flags-other-rule-waived\ntabbed w12 flags-sibling-waiver\ntabbed w5 flags-misspelled-rule\ntabbed w7 pass-exact-rule\ntabbed w9 pass-nested-depth\ntabbed w10 pass-star\ntabbed w11 pass-rule-list\ntabbed w4 pass-self-waiver\nquote-track"}],"result":[{"id":"bounce-easing","snippet":"cubic-bezier(0.3, 1.8, 0.6, 1)","selector":{"$undef":true}},{"id":"marquee","snippet":".quote-track — infinite horizontal loop animation \"ticker-loop\"","selector":".quote-track"},{"id":"codex-grid-background","snippet":"two-axis grid-line gradient background","selector":"/* Dead grid CSS: this two-axis hairline grid selector matches nothing on this page, so the browser pass must drop the codex-grid finding — the pattern never renders here. */ .never-rendered-grid"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Fixture: gradient body ground, oklch stops (low-contrast)</title>\n<style>\n /* The impeccable.style false-positive class: the page ground is a gradient\n set via `background:` shorthand, so the body's computed backgroundColor\n is transparent and the ground exists only as a backgroundImage whose\n stops serialize in oklch. The detector must measure text against those\n stops (composited under any translucent layers), never against an\n assumed white body. */\n body {\n margin: 0;\n padding: 48px;\n background: linear-gradient(180deg, oklch(7% 0.006 95), oklch(4% 0.004 95));\n font-family: Arial, sans-serif;\n }\n body > * { margin: 0 0 32px; }\n\n /* ── Should flag ─────────────────────────────────────────────────── */\n .flag-muted-direct {\n width: 560px;\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .flag-muted-nested-wrapper { width: 560px; }\n .flag-muted-nested {\n color: oklch(32% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n .frosted-light {\n width: 480px;\n padding: 24px;\n background: rgba(250, 249, 246, 0.88);\n border-radius: 4px;\n }\n .flag-light-on-frosted {\n color: oklch(88% 0 0);\n font-size: 15px;\n line-height: 1.6;\n margin: 0;\n }\n\n /* ── Should pass ─────────────────────────────────────────────────── */\n .pass-hero-title {\n width: 640px;\n color: oklch(91% 0 0);\n font-size: 48px;\n }\n .pass-hero-body {\n width: 560px;\n color: oklch(88% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .pass-on-raised {\n width: 420px;\n padding: 24px;\n background: oklch(11% 0.006 95);\n color: oklch(88% 0 0);\n font-size: 15px;\n }\n .pass-dark-on-frosted {\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n margin: 0 0 12px;\n }\n .pass-hex-gradient {\n width: 520px;\n padding: 24px;\n background-image: linear-gradient(180deg, #14120f, #0a0908);\n color: oklch(90% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-image-backed {\n width: 460px;\n padding: 18px 22px;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2)), url(\"/fixtures/antipatterns/no-such-texture.png\") center / cover;\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-photo-panel {\n width: 460px;\n padding: 18px 22px;\n background-image: url(\"/fixtures/antipatterns/no-such-photo.jpg\");\n color: oklch(20% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-hidden-scene {\n opacity: 0;\n width: 560px;\n }\n .pass-hidden-scene p {\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n</style>\n</head>\n<body>\n <h1 class=\"pass-hero-title\">Light display text on the lacquer gradient</h1>\n <p class=\"pass-hero-body\">This paragraph sits directly on the body's dark gradient ground. Its near-white ink clears WCAG AA against both gradient stops by a wide margin, so a detector that measures the real ground must not flag it. A detector that assumes a white body flags it at about 1.3:1.</p>\n <div class=\"pass-on-raised\">Light text on a raised solid oklch surface between the text and the body gradient still resolves through the normal opaque-ancestor walk.</div>\n <div class=\"frosted-light\">\n <p class=\"pass-dark-on-frosted\">Dark ink on a translucent light wash over the dark gradient. The wash composites to a light surface, so this passes; measuring raw gradient stops would wrongly flag it.</p>\n <p class=\"flag-light-on-frosted\">Light ink on the same translucent light wash genuinely fails once the wash is composited over the stops; raw stops would wrongly pass it.</p>\n </div>\n <p class=\"flag-muted-direct\">This muted dark-gray ink genuinely fails contrast against both stops of the dark gradient ground, which proves the gradient stops are being measured instead of the checks silently skipping.</p>\n <div class=\"flag-muted-nested-wrapper\">\n <p class=\"flag-muted-nested\">Muted ink reached through a transparent wrapper still resolves to the body gradient and still fails against its stops.</p>\n </div>\n <div class=\"pass-hex-gradient\">A non-body gradient with legacy hex stops keeps working through the original rgb/hex parser, and light text on it passes.</div>\n <div class=\"pass-image-backed\">Dark ink on a translucent wash over a texture image. The image pixels are unknowable to the analytic walk, so contrast must skip rather than composite the wash over the wrong base.</div>\n <div class=\"pass-photo-panel\">Dark ink on a photo-backed panel sitting over the dark body gradient. Walking past the photo to the gradient stops would wrongly flag this; the unmeasurable image layer must end the walk.</div>\n <div class=\"pass-hidden-scene\"><p>Muted ink inside an opacity-zero scene deck. Nobody sees this state, so the contrast checks must not measure it.</p></div>\n</body>\n</html>\n",{"styleText":"\n /* The impeccable.style false-positive class: the page ground is a gradient\n set via `background:` shorthand, so the body's computed backgroundColor\n is transparent and the ground exists only as a backgroundImage whose\n stops serialize in oklch. The detector must measure text against those\n stops (composited under any translucent layers), never against an\n assumed white body. */\n body {\n margin: 0;\n padding: 48px;\n background: linear-gradient(180deg, oklch(7% 0.006 95), oklch(4% 0.004 95));\n font-family: Arial, sans-serif;\n }\n body > * { margin: 0 0 32px; }\n\n /* ── Should flag ─────────────────────────────────────────────────── */\n .flag-muted-direct {\n width: 560px;\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .flag-muted-nested-wrapper { width: 560px; }\n .flag-muted-nested {\n color: oklch(32% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n .frosted-light {\n width: 480px;\n padding: 24px;\n background: rgba(250, 249, 246, 0.88);\n border-radius: 4px;\n }\n .flag-light-on-frosted {\n color: oklch(88% 0 0);\n font-size: 15px;\n line-height: 1.6;\n margin: 0;\n }\n\n /* ── Should pass ─────────────────────────────────────────────────── */\n .pass-hero-title {\n width: 640px;\n color: oklch(91% 0 0);\n font-size: 48px;\n }\n .pass-hero-body {\n width: 560px;\n color: oklch(88% 0 0);\n font-size: 16px;\n line-height: 1.7;\n }\n .pass-on-raised {\n width: 420px;\n padding: 24px;\n background: oklch(11% 0.006 95);\n color: oklch(88% 0 0);\n font-size: 15px;\n }\n .pass-dark-on-frosted {\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n margin: 0 0 12px;\n }\n .pass-hex-gradient {\n width: 520px;\n padding: 24px;\n background-image: linear-gradient(180deg, #14120f, #0a0908);\n color: oklch(90% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-image-backed {\n width: 460px;\n padding: 18px 22px;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2)), url(\"/fixtures/antipatterns/no-such-texture.png\") center / cover;\n color: oklch(15% 0.01 95);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-photo-panel {\n width: 460px;\n padding: 18px 22px;\n background-image: url(\"/fixtures/antipatterns/no-such-photo.jpg\");\n color: oklch(20% 0 0);\n font-size: 15px;\n line-height: 1.6;\n }\n .pass-hidden-scene {\n opacity: 0;\n width: 560px;\n }\n .pass-hidden-scene p {\n color: oklch(30% 0 0);\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n }\n","classText":"pass-hero-title\npass-hero-body\npass-on-raised\nfrosted-light\npass-dark-on-frosted\nflag-light-on-frosted\nflag-muted-direct\nflag-muted-nested-wrapper\nflag-muted-nested\npass-hex-gradient\npass-image-backed\npass-photo-panel\npass-hidden-scene"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Legitimate Border Patterns — Should NOT Flag</title>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }\n .demo { max-width: 36rem; margin-bottom: 1.5rem; }\n </style>\n</head>\n<body>\n <h1>Legitimate Border Patterns</h1>\n <p style=\"color: #6b7280; margin-bottom: 2rem;\">Every border here is a well-established web pattern. None should be flagged.</p>\n\n <!-- 1. BLOCKQUOTE — the classic border-left usage -->\n <h2>Blockquotes</h2>\n <div class=\"demo\">\n <blockquote style=\"border-left: 4px solid #d1d5db; padding-left: 1rem; margin: 0; color: #4b5563; font-style: italic;\">\n \"Design is not just what it looks like and feels like. Design is how it works.\"\n </blockquote>\n </div>\n\n <!-- 2. SIDEBAR NAV — active state indicator -->\n <h2>Sidebar Navigation (Active State)</h2>\n <div class=\"demo\">\n <nav style=\"width: 200px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;\">\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\">Dashboard</a>\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #1d4ed8; text-decoration: none; border-left: 3px solid #3b82f6; background: #eff6ff; font-weight: 500;\">Projects</a>\n <a href=\"#\" style=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\">Settings</a>\n </nav>\n </div>\n\n <!-- 3. FORM VALIDATION — error indicator on input -->\n <h2>Form Validation</h2>\n <div class=\"demo\">\n <div style=\"margin-bottom: 1rem;\">\n <label style=\"display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem;\">Email</label>\n <input type=\"email\" value=\"not-an-email\" style=\"width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #fca5a5; border-left: 3px solid #b91c1c; border-radius: 6px; outline: none; font-size: 0.875rem;\">\n <p style=\"color: #b91c1c; font-size: 0.75rem; margin-top: 0.25rem;\">Please enter a valid email address.</p>\n </div>\n </div>\n\n <!-- 4. TIMELINE / STEPPER -->\n <h2>Timeline</h2>\n <div class=\"demo\">\n <div style=\"border-left: 3px solid #e5e7eb; padding-left: 1.5rem; margin-left: 0.75rem;\">\n <div style=\"position: relative; padding-bottom: 1.5rem;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0;\">Order placed</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">March 15, 2026</p>\n </div>\n <div style=\"position: relative; padding-bottom: 1.5rem;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0;\">Shipped</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">March 16, 2026</p>\n </div>\n <div style=\"position: relative;\">\n <div style=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #d1d5db; border: 2px solid white;\"></div>\n <p style=\"font-weight: 500; margin: 0; color: #4b5563;\">Delivered</p>\n <p style=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\">Expected March 18</p>\n </div>\n </div>\n </div>\n\n <!-- 5. CODE BLOCK — diff highlighting -->\n <h2>Code Diff Highlighting</h2>\n <div class=\"demo\">\n <pre style=\"background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-size: 0.8125rem; overflow-x: auto; margin: 0;\">\n<code><span style=\"display: block; border-left: 3px solid #ef4444; padding-left: 0.75rem; background: rgba(239,68,68,0.1);\">- const old = getValue();</span>\n<span style=\"display: block; border-left: 3px solid #22c55e; padding-left: 0.75rem; background: rgba(34,197,94,0.1);\">+ const value = getNewValue();</span>\n<span style=\"display: block; padding-left: calc(3px + 0.75rem);\"> return value;</span></code></pre>\n </div>\n\n <!-- 6. TAB ACTIVE STATE — bottom border -->\n <h2>Tab Navigation</h2>\n <div class=\"demo\">\n <div style=\"display: flex; border-bottom: 1px solid #e5e7eb;\">\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #1d4ed8; border-bottom: 2px solid #3b82f6; font-weight: 500; cursor: pointer;\">Overview</button>\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\">Analytics</button>\n <button style=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\">Settings</button>\n </div>\n </div>\n\n <!-- 7. TABLE CELL BORDERS -->\n <h2>Data Table</h2>\n <div class=\"demo\">\n <table style=\"width: 100%; border-collapse: collapse; font-size: 0.875rem;\">\n <thead>\n <tr>\n <th style=\"text-align: left; padding: 0.5rem; border-bottom: 2px solid #111827;\">Name</th>\n <th style=\"text-align: right; padding: 0.5rem; border-bottom: 2px solid #111827;\">Revenue</th>\n </tr>\n </thead>\n <tbody>\n <tr><td style=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">Acme Corp</td><td style=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">$1.2M</td></tr>\n <tr><td style=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">Globex</td><td style=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\">$850K</td></tr>\n </tbody>\n </table>\n </div>\n\n <!-- 8. ALERT BANNER — full-width, not a card -->\n <h2>Alert Banner</h2>\n <div class=\"demo\">\n <div style=\"border: 1px solid #f59e0b; background: #fffbeb; padding: 0.75rem 1rem; font-size: 0.875rem; color: #92400e;\">\n <strong>Warning:</strong> Your trial expires in 3 days. <a href=\"#\" style=\"color: #d97706;\">Upgrade now</a>\n </div>\n </div>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }\n h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }\n h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }\n .demo { max-width: 36rem; margin-bottom: 1.5rem; }\n \nstyle=\"color: #6b7280; margin-bottom: 2rem;\"\nstyle=\"border-left: 4px solid #d1d5db; padding-left: 1rem; margin: 0; color: #4b5563; font-style: italic;\"\nstyle=\"width: 200px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #1d4ed8; text-decoration: none; border-left: 3px solid #3b82f6; background: #eff6ff; font-weight: 500;\"\nstyle=\"display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;\"\nstyle=\"margin-bottom: 1rem;\"\nstyle=\"display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem;\"\nstyle=\"width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #fca5a5; border-left: 3px solid #b91c1c; border-radius: 6px; outline: none; font-size: 0.875rem;\"\nstyle=\"color: #b91c1c; font-size: 0.75rem; margin-top: 0.25rem;\"\nstyle=\"border-left: 3px solid #e5e7eb; padding-left: 1.5rem; margin-left: 0.75rem;\"\nstyle=\"position: relative; padding-bottom: 1.5rem;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"position: relative; padding-bottom: 1.5rem;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"position: relative;\"\nstyle=\"position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #d1d5db; border: 2px solid white;\"\nstyle=\"font-weight: 500; margin: 0; color: #4b5563;\"\nstyle=\"font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;\"\nstyle=\"background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-size: 0.8125rem; overflow-x: auto; margin: 0;\"\nstyle=\"display: block; border-left: 3px solid #ef4444; padding-left: 0.75rem; background: rgba(239,68,68,0.1);\"\nstyle=\"display: block; border-left: 3px solid #22c55e; padding-left: 0.75rem; background: rgba(34,197,94,0.1);\"\nstyle=\"display: block; padding-left: calc(3px + 0.75rem);\"\nstyle=\"display: flex; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #1d4ed8; border-bottom: 2px solid #3b82f6; font-weight: 500; cursor: pointer;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\"\nstyle=\"padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;\"\nstyle=\"width: 100%; border-collapse: collapse; font-size: 0.875rem;\"\nstyle=\"text-align: left; padding: 0.5rem; border-bottom: 2px solid #111827;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 2px solid #111827;\"\nstyle=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;\"\nstyle=\"border: 1px solid #f59e0b; background: #fffbeb; padding: 0.75rem 1rem; font-size: 0.875rem; color: #92400e;\"\nstyle=\"color: #d97706;\"","classText":"demo\ndemo\ndemo\ndemo\ndemo\ndemo\ndemo\ndemo"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Side-Tab with Modern Color Formats (oklch/lab/lch/hsl/hwb)</title>\n <style>\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n The side-tab snippet doesn't embed identifying text, so the test\n asserts finding counts against the structural ids (#flag-N / #pass-N)\n by reading the element via data attributes in the check path. Since\n that isn't available to the Node fixture runner, we instead place a\n unique number of FLAG elements (each a distinct side-tab) and verify\n the side-tab count. */\n\n /* CSS custom properties — variables used by the var()-case fixtures.\n jsdom's CSSOM drops any border shorthand containing var(), which used\n to make every side-tab built with CSS variables invisible to the\n detector. These cases regression-test the fallback pass that parses\n border shorthands directly off the rule (not the computed style). */\n :root {\n --brand: #87a8ff;\n --warn: #8a6a2f;\n --terracotta: #aa674d;\n --line: #e5e7eb; /* neutral gray — must NOT trigger */\n --rule: #f3f4f6; /* near-white — must NOT trigger */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using modern color formats ── */\n\n /* 1: oklch border-left + border-radius (the canonical real-page bug) */\n #flag-oklch-1 {\n width: 400px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n }\n\n /* 2: oklch mixed — neutral 1px border all around + colored 4px left +\n border-radius (mirrors .medication-card from the reproducer) */\n #flag-oklch-2 {\n width: 400px;\n background: oklch(100% 0 0);\n border: 1px solid oklch(80% 0.05 250);\n border-left: 4px solid oklch(75% 0.15 45);\n border-radius: 4px;\n }\n\n /* 3: oklch alert pattern — colored 4px border-left + border-radius */\n #flag-oklch-3 {\n width: 400px;\n background: oklch(98% 0.01 60);\n border-left: 4px solid oklch(75% 0.18 45);\n border-radius: 4px;\n }\n\n /* 4: oklab border-left + border-radius */\n #flag-oklab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid oklab(60% 0.1 0.15);\n }\n\n /* 5: lch border-left + border-radius */\n #flag-lch-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lch(55% 60 250);\n }\n\n /* 6: lab border-left + border-radius */\n #flag-lab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lab(50% 40 -30);\n }\n\n /* 7: card-shaped <label> with colored oklch side border + radius\n (the checklist-item pattern from preop-portal reproducer) */\n label#flag-label-oklch {\n display: grid;\n grid-template-columns: auto 1fr;\n width: 400px;\n padding: 16px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n cursor: pointer;\n }\n\n /* 8: card-shaped <label> with colored rgb side border + radius —\n proves the fix is not oklch-specific */\n label#flag-label-rgb {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid #3b82f6;\n cursor: pointer;\n }\n\n /* 9: CSS variable in shorthand — the canonical real-page pattern from\n evals (e.g. 09-physics-study: border-left:8px solid var(--brand-2)).\n jsdom silently drops this entire declaration, so the computed-style\n path used to miss it. */\n #flag-var-shorthand {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid var(--brand);\n }\n\n /* 10: CSS variable + neutral 1px all-around border — mirrors the\n medication-card pattern with a colored accent on the left. */\n #flag-var-mixed {\n width: 400px;\n background: #ffffff;\n border: 1px solid var(--line);\n border-left: 4px solid var(--warn);\n border-radius: 4px;\n }\n\n /* 11: CSS variable shorthand, right side. Ensures the fallback works\n for both inline axes, not just border-left. */\n #flag-var-right {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 4px solid var(--terracotta);\n }\n\n /* 12: card-shaped <label> with var() side border + radius —\n the checklist-row shape, built with tokens. */\n label#flag-label-var {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid var(--brand);\n cursor: pointer;\n }\n\n /* ── PASS cases: legitimate or neutral borders, must NOT fire ── */\n\n /* 1: oklch chroma ~0 — true neutral gray border should not fire */\n #pass-oklch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(80% 0 0);\n }\n\n /* 2: oklch low chroma (0.01) — still perceptually neutral */\n #pass-oklch-nearneutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(75% 0.01 250);\n }\n\n /* 3: oklch colored border all around (not one-sided) — no side-tab */\n #pass-oklch-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid oklch(65% 0.12 250);\n border-radius: 4px;\n }\n\n /* 4: oklch colored 1px border-left (too thin to qualify) */\n #pass-oklch-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid oklch(65% 0.12 250);\n }\n\n /* 5: lab neutral (chroma near zero) — must not fire */\n #pass-lab-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lab(50% 0 0);\n }\n\n /* 6: lch neutral (chroma 0) */\n #pass-lch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lch(50% 0 0);\n }\n\n /* 7: plain inline form label — no border, no padding, no radius.\n Must NOT be flagged for anything. */\n label#pass-label-plain {\n font-size: 14px;\n font-weight: 500;\n color: #374151;\n }\n\n /* 8: form label with a small neutral gray left border — neutral color\n should not fire the colored side-tab rule. */\n label#pass-label-neutral-border {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid #e5e7eb;\n }\n\n /* 9: inline label inside a form row — default inline layout,\n no borders, must not fire. */\n label#pass-label-inline {\n font-size: 13px;\n color: #6b7280;\n margin-right: 8px;\n }\n\n /* 10: label with 1px colored left border — too thin to qualify\n (matches the existing 1px thin rule for divs). */\n label#pass-label-thin {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid #3b82f6;\n }\n\n /* 11: CSS variable resolving to a neutral gray — must NOT fire,\n even though the border shorthand contains var(). */\n #pass-var-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid var(--line);\n }\n\n /* 12: thin (1px) var-based left border — under the side-tab width\n threshold even when resolved. */\n #pass-var-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid var(--brand);\n }\n\n /* 13: var-based uniform border on all four sides. Not a side-tab. */\n #pass-var-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid var(--brand);\n border-radius: 4px;\n }\n\n /* 14: neutral 1px oklch right divider — context-bar hairline, not a side-tab. */\n #pass-oklch-neutral-right-divider {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 1px solid oklch(92% 0 0 / 0.12);\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"case\" id=\"flag-oklch-1\"><h3>oklch checklist</h3><p>oklch border-left 3px + radius</p></div>\n <div class=\"case\" id=\"flag-oklch-2\"><h3>oklch medication card</h3><p>neutral all + colored left 4px</p></div>\n <div class=\"case\" id=\"flag-oklch-3\"><h3>oklch alert</h3><p>warm border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-oklab-1\"><h3>oklab card</h3><p>oklab border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-lch-1\"><h3>lch card</h3><p>lch border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-lab-1\"><h3>lab card</h3><p>lab border-left 4px + radius</p></div>\n <label class=\"case\" id=\"flag-label-oklch\">\n <input type=\"checkbox\">\n <span>Card-shaped label with oklch side border</span>\n </label>\n <label class=\"case\" id=\"flag-label-rgb\">\n <input type=\"checkbox\">\n <span>Card-shaped label with rgb side border</span>\n </label>\n <div class=\"case\" id=\"flag-var-shorthand\"><h3>var() shorthand</h3><p>border-left 5px + var(--brand)</p></div>\n <div class=\"case\" id=\"flag-var-mixed\"><h3>var() mixed</h3><p>neutral all + colored var left</p></div>\n <div class=\"case\" id=\"flag-var-right\"><h3>var() right side</h3><p>border-right 4px + var(--terracotta)</p></div>\n <label class=\"case\" id=\"flag-label-var\">\n <input type=\"checkbox\">\n <span>Card-shaped label with var() side border</span>\n </label>\n </div>\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"case\" id=\"pass-oklch-neutral\"><h3>oklch neutral</h3><p>chroma 0 gray</p></div>\n <div class=\"case\" id=\"pass-oklch-nearneutral\"><h3>oklch near-neutral</h3><p>chroma 0.01</p></div>\n <div class=\"case\" id=\"pass-oklch-allsides\"><h3>oklch all sides</h3><p>uniform border</p></div>\n <div class=\"case\" id=\"pass-oklch-thin\"><h3>oklch thin</h3><p>1px too thin</p></div>\n <div class=\"case\" id=\"pass-lab-neutral\"><h3>lab neutral</h3><p>chroma 0</p></div>\n <div class=\"case\" id=\"pass-lch-neutral\"><h3>lch neutral</h3><p>chroma 0</p></div>\n <div>\n <label for=\"pass-email\" id=\"pass-label-plain\">Email address</label>\n <input type=\"email\" id=\"pass-email\">\n </div>\n <label id=\"pass-label-neutral-border\">\n <input type=\"checkbox\">\n <span>Label with neutral gray left border</span>\n </label>\n <form>\n <label for=\"pass-name\" id=\"pass-label-inline\">Name:</label>\n <input type=\"text\" id=\"pass-name\">\n </form>\n <label id=\"pass-label-thin\">\n <input type=\"checkbox\">\n <span>Label with 1px colored left border (too thin)</span>\n </label>\n <div class=\"case\" id=\"pass-var-neutral\"><h3>var() neutral</h3><p>--line resolves to gray</p></div>\n <div class=\"case\" id=\"pass-var-thin\"><h3>var() thin</h3><p>1px too thin to qualify</p></div>\n <div class=\"case\" id=\"pass-var-allsides\"><h3>var() all sides</h3><p>uniform, not a side-tab</p></div>\n <div class=\"case\" id=\"pass-oklch-neutral-right-divider\"><h3>oklch neutral right divider</h3><p>1px neutral context-bar hairline</p></div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n The side-tab snippet doesn't embed identifying text, so the test\n asserts finding counts against the structural ids (#flag-N / #pass-N)\n by reading the element via data attributes in the check path. Since\n that isn't available to the Node fixture runner, we instead place a\n unique number of FLAG elements (each a distinct side-tab) and verify\n the side-tab count. */\n\n /* CSS custom properties — variables used by the var()-case fixtures.\n jsdom's CSSOM drops any border shorthand containing var(), which used\n to make every side-tab built with CSS variables invisible to the\n detector. These cases regression-test the fallback pass that parses\n border shorthands directly off the rule (not the computed style). */\n :root {\n --brand: #87a8ff;\n --warn: #8a6a2f;\n --terracotta: #aa674d;\n --line: #e5e7eb; /* neutral gray — must NOT trigger */\n --rule: #f3f4f6; /* near-white — must NOT trigger */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using modern color formats ── */\n\n /* 1: oklch border-left + border-radius (the canonical real-page bug) */\n #flag-oklch-1 {\n width: 400px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n }\n\n /* 2: oklch mixed — neutral 1px border all around + colored 4px left +\n border-radius (mirrors .medication-card from the reproducer) */\n #flag-oklch-2 {\n width: 400px;\n background: oklch(100% 0 0);\n border: 1px solid oklch(80% 0.05 250);\n border-left: 4px solid oklch(75% 0.15 45);\n border-radius: 4px;\n }\n\n /* 3: oklch alert pattern — colored 4px border-left + border-radius */\n #flag-oklch-3 {\n width: 400px;\n background: oklch(98% 0.01 60);\n border-left: 4px solid oklch(75% 0.18 45);\n border-radius: 4px;\n }\n\n /* 4: oklab border-left + border-radius */\n #flag-oklab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid oklab(60% 0.1 0.15);\n }\n\n /* 5: lch border-left + border-radius */\n #flag-lch-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lch(55% 60 250);\n }\n\n /* 6: lab border-left + border-radius */\n #flag-lab-1 {\n width: 400px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid lab(50% 40 -30);\n }\n\n /* 7: card-shaped <label> with colored oklch side border + radius\n (the checklist-item pattern from preop-portal reproducer) */\n label#flag-label-oklch {\n display: grid;\n grid-template-columns: auto 1fr;\n width: 400px;\n padding: 16px;\n background: oklch(100% 0 0);\n border-radius: 4px;\n border-left: 3px solid oklch(65% 0.12 250);\n cursor: pointer;\n }\n\n /* 8: card-shaped <label> with colored rgb side border + radius —\n proves the fix is not oklch-specific */\n label#flag-label-rgb {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid #3b82f6;\n cursor: pointer;\n }\n\n /* 9: CSS variable in shorthand — the canonical real-page pattern from\n evals (e.g. 09-physics-study: border-left:8px solid var(--brand-2)).\n jsdom silently drops this entire declaration, so the computed-style\n path used to miss it. */\n #flag-var-shorthand {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid var(--brand);\n }\n\n /* 10: CSS variable + neutral 1px all-around border — mirrors the\n medication-card pattern with a colored accent on the left. */\n #flag-var-mixed {\n width: 400px;\n background: #ffffff;\n border: 1px solid var(--line);\n border-left: 4px solid var(--warn);\n border-radius: 4px;\n }\n\n /* 11: CSS variable shorthand, right side. Ensures the fallback works\n for both inline axes, not just border-left. */\n #flag-var-right {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 4px solid var(--terracotta);\n }\n\n /* 12: card-shaped <label> with var() side border + radius —\n the checklist-row shape, built with tokens. */\n label#flag-label-var {\n display: block;\n width: 400px;\n padding: 16px;\n background: #ffffff;\n border-radius: 6px;\n border-left: 4px solid var(--brand);\n cursor: pointer;\n }\n\n /* ── PASS cases: legitimate or neutral borders, must NOT fire ── */\n\n /* 1: oklch chroma ~0 — true neutral gray border should not fire */\n #pass-oklch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(80% 0 0);\n }\n\n /* 2: oklch low chroma (0.01) — still perceptually neutral */\n #pass-oklch-nearneutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid oklch(75% 0.01 250);\n }\n\n /* 3: oklch colored border all around (not one-sided) — no side-tab */\n #pass-oklch-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid oklch(65% 0.12 250);\n border-radius: 4px;\n }\n\n /* 4: oklch colored 1px border-left (too thin to qualify) */\n #pass-oklch-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid oklch(65% 0.12 250);\n }\n\n /* 5: lab neutral (chroma near zero) — must not fire */\n #pass-lab-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lab(50% 0 0);\n }\n\n /* 6: lch neutral (chroma 0) */\n #pass-lch-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid lch(50% 0 0);\n }\n\n /* 7: plain inline form label — no border, no padding, no radius.\n Must NOT be flagged for anything. */\n label#pass-label-plain {\n font-size: 14px;\n font-weight: 500;\n color: #374151;\n }\n\n /* 8: form label with a small neutral gray left border — neutral color\n should not fire the colored side-tab rule. */\n label#pass-label-neutral-border {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid #e5e7eb;\n }\n\n /* 9: inline label inside a form row — default inline layout,\n no borders, must not fire. */\n label#pass-label-inline {\n font-size: 13px;\n color: #6b7280;\n margin-right: 8px;\n }\n\n /* 10: label with 1px colored left border — too thin to qualify\n (matches the existing 1px thin rule for divs). */\n label#pass-label-thin {\n display: block;\n width: 400px;\n padding: 8px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid #3b82f6;\n }\n\n /* 11: CSS variable resolving to a neutral gray — must NOT fire,\n even though the border shorthand contains var(). */\n #pass-var-neutral {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid var(--line);\n }\n\n /* 12: thin (1px) var-based left border — under the side-tab width\n threshold even when resolved. */\n #pass-var-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid var(--brand);\n }\n\n /* 13: var-based uniform border on all four sides. Not a side-tab. */\n #pass-var-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid var(--brand);\n border-radius: 4px;\n }\n\n /* 14: neutral 1px oklch right divider — context-bar hairline, not a side-tab. */\n #pass-oklch-neutral-right-divider {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-right: 1px solid oklch(92% 0 0 / 0.12);\n }\n ","classText":"grid\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Side-Tab with CSS Named Colors (purple/rebeccapurple/crimson/teal)</title>\n <style>\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n Regression for issue #359: `border-left: 4px solid purple` in a .html\n file never fired side-tab because the static cascade's shorthand color\n extraction only recognized 9 named colors — every other spec name\n (purple, rebeccapurple, crimson, teal, ...) was dropped from the\n shorthand, leaving the side at the default black, which reads as\n neutral and silently skips the check. The same declaration in a .css\n file was flagged by the regex engine, so the two engines disagreed. */\n\n :root {\n --accent: orange; /* named color behind a var() — must resolve and flag */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using CSS named colors ── */\n\n /* 1: the exact reproducer from issue #359 — named purple in a <style>\n rule with background + padding + radius */\n #flag-named-purple {\n width: 400px;\n background: #f6f6f6;\n padding: 16px;\n border-left: 4px solid purple;\n border-radius: 8px;\n }\n\n /* 2: rebeccapurple — a longer name that contains another color name\n (\"purple\") as a substring; must match whole-token. Width 5px so the\n finding snippet is unique to this case. */\n #flag-named-rebecca {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid rebeccapurple;\n }\n\n /* 3: horizontal stripe variant — named crimson riding the top edge */\n #flag-named-crimson-top {\n width: 400px;\n background: #ffffff;\n border-top: 4px solid crimson;\n }\n\n /* 4: named teal, 3px, no radius — the bare w >= 3 arm */\n #flag-named-teal {\n width: 400px;\n background: #ffffff;\n border-left: 3px solid teal;\n }\n\n /* 5: named color behind a var() in the shorthand. Width 6px so the\n finding snippet is unique to this case. */\n #flag-named-var {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 6px solid var(--accent);\n }\n\n /* ── PASS cases: neutral named colors and non-side-tab shapes ── */\n\n /* 1: dimgray — chromatic-looking name, neutral value; must NOT fire */\n #pass-named-dimgray {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid dimgray;\n }\n\n /* 2: gainsboro — light neutral named color */\n #pass-named-gainsboro {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid gainsboro;\n }\n\n /* 3: named black side border — neutral, not a colored stripe */\n #pass-named-black {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid black;\n }\n\n /* 4: 1px named purple — too thin to qualify */\n #pass-named-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid purple;\n }\n\n /* 5: uniform named purple border on all four sides — not a side-tab */\n #pass-named-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid purple;\n border-radius: 4px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n <div class=\"case\" id=\"flag-named-purple\"><h3>named purple</h3><p>border-left 4px solid purple + radius</p></div>\n <div class=\"case\" id=\"flag-named-rebecca\"><h3>rebeccapurple</h3><p>border-left 4px + radius</p></div>\n <div class=\"case\" id=\"flag-named-crimson-top\"><h3>crimson top stripe</h3><p>border-top 4px, horizontal variant</p></div>\n <div class=\"case\" id=\"flag-named-teal\"><h3>named teal</h3><p>border-left 3px, no radius</p></div>\n <div class=\"case\" id=\"flag-named-var\"><h3>var() to named</h3><p>border-left 4px solid var(--accent)</p></div>\n <div class=\"case\" style=\"width: 400px; background: #ffffff; border-left: 7px solid purple\">\n <h3>inline named purple</h3><p>style attribute, issue #359 case (a); width 7px keeps the snippet unique</p>\n </div>\n </div>\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n <div class=\"case\" id=\"pass-named-dimgray\"><h3>dimgray</h3><p>neutral named color</p></div>\n <div class=\"case\" id=\"pass-named-gainsboro\"><h3>gainsboro</h3><p>light neutral named color</p></div>\n <div class=\"case\" id=\"pass-named-black\"><h3>named black</h3><p>neutral side border</p></div>\n <div class=\"case\" id=\"pass-named-thin\"><h3>1px purple</h3><p>too thin to qualify</p></div>\n <div class=\"case\" id=\"pass-named-allsides\"><h3>uniform purple</h3><p>all four sides, not a stripe</p></div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left col = should-flag, right col = should-pass.\n Regression for issue #359: `border-left: 4px solid purple` in a .html\n file never fired side-tab because the static cascade's shorthand color\n extraction only recognized 9 named colors — every other spec name\n (purple, rebeccapurple, crimson, teal, ...) was dropped from the\n shorthand, leaving the side at the default black, which reads as\n neutral and silently skips the check. The same declaration in a .css\n file was flagged by the regex engine, so the two engines disagreed. */\n\n :root {\n --accent: orange; /* named color behind a var() — must resolve and flag */\n }\n\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { padding: 12px 16px; margin-bottom: 16px; }\n .case h3 { font-size: 14px; margin: 0 0 4px; }\n .case p { font-size: 13px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases: colored side-tab borders using CSS named colors ── */\n\n /* 1: the exact reproducer from issue #359 — named purple in a <style>\n rule with background + padding + radius */\n #flag-named-purple {\n width: 400px;\n background: #f6f6f6;\n padding: 16px;\n border-left: 4px solid purple;\n border-radius: 8px;\n }\n\n /* 2: rebeccapurple — a longer name that contains another color name\n (\"purple\") as a substring; must match whole-token. Width 5px so the\n finding snippet is unique to this case. */\n #flag-named-rebecca {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 5px solid rebeccapurple;\n }\n\n /* 3: horizontal stripe variant — named crimson riding the top edge */\n #flag-named-crimson-top {\n width: 400px;\n background: #ffffff;\n border-top: 4px solid crimson;\n }\n\n /* 4: named teal, 3px, no radius — the bare w >= 3 arm */\n #flag-named-teal {\n width: 400px;\n background: #ffffff;\n border-left: 3px solid teal;\n }\n\n /* 5: named color behind a var() in the shorthand. Width 6px so the\n finding snippet is unique to this case. */\n #flag-named-var {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 6px solid var(--accent);\n }\n\n /* ── PASS cases: neutral named colors and non-side-tab shapes ── */\n\n /* 1: dimgray — chromatic-looking name, neutral value; must NOT fire */\n #pass-named-dimgray {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid dimgray;\n }\n\n /* 2: gainsboro — light neutral named color */\n #pass-named-gainsboro {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 3px solid gainsboro;\n }\n\n /* 3: named black side border — neutral, not a colored stripe */\n #pass-named-black {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 4px solid black;\n }\n\n /* 4: 1px named purple — too thin to qualify */\n #pass-named-thin {\n width: 400px;\n background: #ffffff;\n border-radius: 4px;\n border-left: 1px solid purple;\n }\n\n /* 5: uniform named purple border on all four sides — not a side-tab */\n #pass-named-allsides {\n width: 400px;\n background: #ffffff;\n border: 3px solid purple;\n border-radius: 4px;\n }\n \nstyle=\"width: 400px; background: #ffffff; border-left: 7px solid purple\"","classText":"grid\ncol\ncase\ncase\ncase\ncase\ncase\ncase\ncol\ncase\ncase\ncase\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography Anti-Patterns — Should Flag</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n font-family: 'Inter', sans-serif;\n background: #f9fafb;\n padding: 2rem;\n }\n h1 { font-size: 18px; font-weight: 700; margin-bottom: 0.5rem; }\n h2 { font-size: 16px; font-weight: 600; margin: 2rem 0 0.75rem; color: #6b7280; }\n h3 { font-size: 15px; font-weight: 600; }\n p { font-size: 14px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 13px; color: #6b7280; }\n </style>\n</head>\n<body>\n <h1>Typography Anti-Patterns</h1>\n <p>This page triggers three typography detections:</p>\n\n <h2>1. Overused Font</h2>\n <p>Inter is loaded from Google Fonts and set as the only font-family. It's the most common AI default.</p>\n\n <h2>2. Single Font</h2>\n <p>There's no second font for headings or display text. Everything uses Inter — no typographic variety.</p>\n\n <h2>3. Flat Type Hierarchy</h2>\n <p>The font sizes are 13px, 14px, 15px, 16px, 18px — all crammed into a 5px range. No visual contrast between heading and body.</p>\n <p class=\"caption\">This caption is barely distinguishable from body text.</p>\n\n <h3>A Subheading</h3>\n <p>Can you tell this is a subheading? Exactly.</p>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: 'Inter', sans-serif;\n background: #f9fafb;\n padding: 2rem;\n }\n h1 { font-size: 18px; font-weight: 700; margin-bottom: 0.5rem; }\n h2 { font-size: 16px; font-weight: 600; margin: 2rem 0 0.75rem; color: #6b7280; }\n h3 { font-size: 15px; font-weight: 600; }\n p { font-size: 14px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 13px; color: #6b7280; }\n ","classText":"caption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography Anti-Patterns — Side by Side</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Newsreader:opsz,wght@6..72,400;6..72,700&family=Karla:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 16px/1.5 system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col {\n min-width: 0;\n }\n\n .col-label {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .flag-type {\n font-family: 'Inter', sans-serif;\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .flag-type h1 {\n margin: 0 0 8px;\n font-size: 18px;\n line-height: 1.2;\n }\n\n .flag-type h2 {\n margin: 24px 0 8px;\n color: #4b5563;\n font-size: 16px;\n line-height: 1.25;\n }\n\n .flag-type h3 {\n margin: 18px 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .flag-type p {\n margin: 0 0 10px;\n color: #4b5563;\n font-size: 14px;\n }\n\n .flag-type .caption {\n color: #6b7280;\n font-size: 13px;\n }\n\n .flag-type .tight-leading-demo {\n line-height: 1.05;\n }\n\n .flag-type .tiny-body-demo {\n color: #374151;\n font-size: 10px;\n line-height: 1.6;\n }\n\n .flag-type .caps-body-demo {\n text-transform: uppercase;\n }\n\n .flag-type .tracked-body-demo {\n letter-spacing: 0.08em;\n }\n\n .flag-type .justified-body-demo {\n text-align: justify;\n }\n\n .pass-type {\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .pass-type h1,\n .pass-type h2 {\n font-family: 'Newsreader', Georgia, serif;\n }\n\n .pass-type h1 {\n margin: 0 0 10px;\n font-size: 48px;\n line-height: 0.98;\n font-weight: 700;\n }\n\n .pass-type h2 {\n margin: 32px 0 10px;\n font-size: 30px;\n line-height: 1.08;\n font-weight: 700;\n }\n\n .pass-type h3,\n .pass-type p,\n .pass-type .caption {\n font-family: 'Karla', system-ui, sans-serif;\n }\n\n .pass-type h3 {\n margin: 22px 0 6px;\n font-size: 22px;\n line-height: 1.15;\n }\n\n .pass-type p {\n margin: 0 0 12px;\n color: #374151;\n font-size: 16px;\n }\n\n .pass-type .caption {\n color: #4b5563;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <section class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n <article class=\"flag-type\">\n <h1>Typography Anti-Patterns</h1>\n <p>This side uses element-level typography failures that can be highlighted directly in the detector lab.</p>\n\n <h2>Tight Leading</h2>\n <p class=\"tight-leading-demo\">This paragraph has a line-height that is too tight for body copy. The lines press into each other and make scanning harder, especially once the text wraps across multiple lines in a production layout.</p>\n\n <h2>Tiny Body Text</h2>\n <p class=\"tiny-body-demo\">This body text is set at 10px, which is too small for real reading content and should be reserved for neither primary copy nor explanatory product text.</p>\n\n <h2>All-Caps Body</h2>\n <p class=\"caps-body-demo\">This entire paragraph is transformed to uppercase, which removes useful word shapes and makes longer body copy feel like a shout instead of a readable passage.</p>\n\n <h2>Wide Tracking</h2>\n <p class=\"tracked-body-demo\">This paragraph spreads every letter apart, a treatment that belongs on short labels at most, not on body copy that people need to read comfortably.</p>\n\n <h2>Justified Text</h2>\n <p class=\"justified-body-demo\">This paragraph uses justified alignment without automatic hyphenation. In narrow columns it creates uneven word spacing and distracting rivers through the text block.</p>\n\n <h2>Overused Font</h2>\n <p>Inter is also the dominant face on this side of the page, so the browser page-level detector has enough real text elements to show the global overused-font banner.</p>\n </article>\n </section>\n\n <section class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n <article class=\"pass-type\">\n <h1>Good Typography</h1>\n <p>This side keeps readable body defaults, a real font pairing, clear scale, and visible hierarchy.</p>\n\n <h2>Two Font Families</h2>\n <p>Newsreader carries display text, while Karla handles reading surfaces and supporting labels.</p>\n\n <h3>Strong Size Hierarchy</h3>\n <p>Sizes range from 12px to 48px with distinct roles for heading, subheading, body, and caption.</p>\n <p class=\"caption\">Caption text is intentionally smaller but still readable.</p>\n </article>\n </section>\n </div>\n\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n padding: 24px;\n background: #f9fafb;\n color: #111827;\n font: 16px/1.5 system-ui, sans-serif;\n }\n\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 32px;\n max-width: 1180px;\n margin: 0 auto;\n }\n\n .col {\n min-width: 0;\n }\n\n .col-label {\n margin: 0 0 14px;\n color: #475569;\n font-size: 13px;\n font-weight: 760;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .flag-type {\n font-family: 'Inter', sans-serif;\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .flag-type h1 {\n margin: 0 0 8px;\n font-size: 18px;\n line-height: 1.2;\n }\n\n .flag-type h2 {\n margin: 24px 0 8px;\n color: #4b5563;\n font-size: 16px;\n line-height: 1.25;\n }\n\n .flag-type h3 {\n margin: 18px 0 4px;\n font-size: 15px;\n line-height: 1.25;\n }\n\n .flag-type p {\n margin: 0 0 10px;\n color: #4b5563;\n font-size: 14px;\n }\n\n .flag-type .caption {\n color: #6b7280;\n font-size: 13px;\n }\n\n .flag-type .tight-leading-demo {\n line-height: 1.05;\n }\n\n .flag-type .tiny-body-demo {\n color: #374151;\n font-size: 10px;\n line-height: 1.6;\n }\n\n .flag-type .caps-body-demo {\n text-transform: uppercase;\n }\n\n .flag-type .tracked-body-demo {\n letter-spacing: 0.08em;\n }\n\n .flag-type .justified-body-demo {\n text-align: justify;\n }\n\n .pass-type {\n background: #fdfdfd;\n padding: 22px;\n border: 1px solid #e5e7eb;\n }\n\n .pass-type h1,\n .pass-type h2 {\n font-family: 'Newsreader', Georgia, serif;\n }\n\n .pass-type h1 {\n margin: 0 0 10px;\n font-size: 48px;\n line-height: 0.98;\n font-weight: 700;\n }\n\n .pass-type h2 {\n margin: 32px 0 10px;\n font-size: 30px;\n line-height: 1.08;\n font-weight: 700;\n }\n\n .pass-type h3,\n .pass-type p,\n .pass-type .caption {\n font-family: 'Karla', system-ui, sans-serif;\n }\n\n .pass-type h3 {\n margin: 22px 0 6px;\n font-size: 22px;\n line-height: 1.15;\n }\n\n .pass-type p {\n margin: 0 0 12px;\n color: #374151;\n font-size: 16px;\n }\n\n .pass-type .caption {\n color: #4b5563;\n font-size: 12px;\n }\n ","classText":"grid\ncol\ncol-label\nflag-type\ntight-leading-demo\ntiny-body-demo\ncaps-body-demo\ntracked-body-demo\njustified-body-demo\ncol\ncol-label\npass-type\ncaption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Typography — Clean Patterns</title>\n <link href=\"https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,700&family=Karla:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n <style>\n body {\n font-family: 'Karla', system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n font-size: 16px;\n color: #111827;\n }\n h1 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 48px;\n font-weight: 700;\n margin-bottom: 0.5rem;\n }\n h2 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 32px;\n font-weight: 700;\n margin: 2rem 0 0.75rem;\n }\n h3 { font-size: 24px; font-weight: 600; }\n p { font-size: 16px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 12px; color: #6b7280; }\n </style>\n</head>\n<body>\n <h1>Good Typography</h1>\n <p>This page uses distinctive fonts, proper pairing, and strong hierarchy.</p>\n\n <h2>Two Font Families</h2>\n <p>Newsreader (serif) for headings, Karla for body. Clear contrast in both structure and personality, and neither face has been adopted into the current AI-default monoculture.</p>\n\n <h3>Strong Size Hierarchy</h3>\n <p>Sizes range from 12px to 48px — a 4:1 ratio with clear visual steps.</p>\n <p class=\"caption\">Caption text is clearly distinct from body.</p>\n<script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: 'Karla', system-ui, sans-serif;\n background: #f9fafb;\n padding: 2rem;\n font-size: 16px;\n color: #111827;\n }\n h1 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 48px;\n font-weight: 700;\n margin-bottom: 0.5rem;\n }\n h2 {\n font-family: 'Newsreader', Georgia, serif;\n font-size: 32px;\n font-weight: 700;\n margin: 2rem 0 0.75rem;\n }\n h3 { font-size: 24px; font-weight: 600; }\n p { font-size: 16px; color: #6b7280; margin-top: 0.25rem; }\n .caption { font-size: 12px; color: #6b7280; }\n ","classText":"caption"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Numbered section marker fixture</title>\n <style>\n .fixture-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n }\n .case {\n margin: 0 0 18px;\n padding: 16px;\n border: 1px solid #d7d0c2;\n border-radius: 8px;\n }\n .marker {\n display: block;\n font: 700 12px/1.1 ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #7c5c00;\n }\n .code-like::before {\n content: \"01 02 03\";\n }\n </style>\n</head>\n<body>\n <main class=\"fixture-grid\">\n <section aria-labelledby=\"flag-title\">\n <h1 id=\"flag-title\">Should flag</h1>\n <article class=\"case\">\n <span class=\"marker\">01</span>\n <h2>Strategy</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">02</span>\n <h2>Prototype</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">03</span>\n <h2>Launch</h2>\n </article>\n <article class=\"case\">\n <span class=\"marker\">04</span>\n <h2>Measure</h2>\n </article>\n </section>\n\n <section aria-labelledby=\"pass-title\">\n <h1 id=\"pass-title\">Should pass</h1>\n <article class=\"case\">\n <h2>Readable date</h2>\n <p>Updated June 14, 2026.</p>\n </article>\n <article class=\"case\">\n <h2>Statistic cards</h2>\n <p>Revenue grew 18%, margin reached 24%, and churn fell to 3%.</p>\n </article>\n <article class=\"case\">\n <h2>CSS generated content</h2>\n <p class=\"code-like\">Generated numbers in CSS should not count as body copy.</p>\n </article>\n <article class=\"case\">\n <h2>Inline SVG coordinates</h2>\n <svg width=\"120\" height=\"32\" viewBox=\"0 0 120 32\" aria-hidden=\"true\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" fill=\"none\" stroke=\"#333\" />\n </svg>\n </article>\n <article class=\"case\">\n <h2>Script constants</h2>\n <script>\n const colorStops = ['01', '02', '03', '04'];\n const luminance = 0.2126 + 0.7152 + 0.0722;\n const size = '11.5px';\n </script>\n <p>Implementation constants in script blocks are invisible.</p>\n </article>\n </section>\n </main>\n</body>\n</html>\n",{"styleText":"\n .fixture-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n }\n .case {\n margin: 0 0 18px;\n padding: 16px;\n border: 1px solid #d7d0c2;\n border-radius: 8px;\n }\n .marker {\n display: block;\n font: 700 12px/1.1 ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #7c5c00;\n }\n .code-like::before {\n content: \"01 02 03\";\n }\n ","classText":"fixture-grid\ncase\nmarker\ncase\nmarker\ncase\nmarker\ncase\nmarker\ncase\ncase\ncase\ncode-like\ncase\ncase"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Numbered Section Labels Fixture</title>\n <style>\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #ffffff;\n color: #222222;\n }\n .column { width: 520px; padding: 20px; }\n h2 { font-size: 28px; margin: 0 0 12px; }\n h3 { font-size: 22px; margin: 0 0 10px; }\n section { margin-bottom: 48px; }\n\n /* ── flag column styles ── */\n .sec-index {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n .sec-index-micro {\n font-family: monospace;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 2px;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n\n /* ── pass column styles ── */\n .big-index {\n font-size: 16px;\n font-weight: 700;\n font-family: monospace;\n display: block;\n }\n .plain-note {\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n color: #666666;\n display: block;\n }\n .step-label {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n display: block;\n }\n .card {\n border: 1px solid #d4d4d4;\n border-radius: 8px;\n padding: 16px;\n }\n </style>\n</head>\n<body>\n\n <!-- ══ SHOULD FLAG: tiny numeric index labels repeated across sections ══ -->\n <div class=\"column\">\n\n <section>\n <span class=\"sec-index\">01</span>\n <h2>Alpha ships first</h2>\n <p>Direct previous-sibling label: tiny, mono, bold, zero-padded index.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">02</span>\n <h2>Beta earns trust</h2>\n <p>Second section repeating the same index scaffold.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">03</span>\n <div>\n <h2>Gamma holds the line</h2>\n <p>Label precedes the wrapper; the heading leads the wrapper.</p>\n </div>\n </section>\n\n <section>\n <span class=\"sec-index-micro\">04 / ROLLOUT</span>\n <h2>Delta closes the loop</h2>\n <p>Index joined to a tracked micro-label by a separator glyph.</p>\n </section>\n\n </div>\n\n <!-- ══ SHOULD PASS ══ -->\n <div class=\"column\">\n\n <section>\n <span class=\"big-index\">05</span>\n <h2>Epsilon reads large</h2>\n <p>16px index is a deliberate display device, not a micro label.</p>\n </section>\n\n <section>\n <span class=\"plain-note\">12 · minute read</span>\n <h2>Zeta stays plain</h2>\n <p>Parses as an index but carries no micro-label styling: regular weight, neutral color, no tracking, serif body face.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">Step 6</span>\n <h2>Eta walks steps</h2>\n <p>Word-first label is not a numeric index.</p>\n </section>\n\n <section>\n <span class=\"sec-index\">7</span>\n <h2>Theta counts casually</h2>\n <p>A bare unpadded single digit is ordinary list numbering.</p>\n </section>\n\n <ol>\n <li>\n <span class=\"step-label\">08</span>\n <h3>Iota lives in a list</h3>\n </li>\n <li>\n <span class=\"step-label\">09</span>\n <h3>Kappa lives in a list too</h3>\n </li>\n </ol>\n\n <article class=\"card\">\n <span class=\"sec-index\">10</span>\n <h3>Lambda sits in a card</h3>\n <p>Per-card indices are item numbering, not section scaffolding.</p>\n </article>\n\n <nav aria-label=\"Progress\">\n <span class=\"sec-index\">11</span>\n <h3>Mu tracks progress</h3>\n </nav>\n\n </div>\n\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #ffffff;\n color: #222222;\n }\n .column { width: 520px; padding: 20px; }\n h2 { font-size: 28px; margin: 0 0 12px; }\n h3 { font-size: 22px; margin: 0 0 10px; }\n section { margin-bottom: 48px; }\n\n /* ── flag column styles ── */\n .sec-index {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n .sec-index-micro {\n font-family: monospace;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 2px;\n color: #b45309;\n display: block;\n margin-bottom: 8px;\n }\n\n /* ── pass column styles ── */\n .big-index {\n font-size: 16px;\n font-weight: 700;\n font-family: monospace;\n display: block;\n }\n .plain-note {\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n color: #666666;\n display: block;\n }\n .step-label {\n font-family: monospace;\n font-size: 11px;\n font-weight: 700;\n display: block;\n }\n .card {\n border: 1px solid #d4d4d4;\n border-radius: 8px;\n padding: 16px;\n }\n ","classText":"column\nsec-index\nsec-index\nsec-index\nsec-index-micro\ncolumn\nbig-index\nplain-note\nsec-index\nsec-index\nstep-label\nstep-label\ncard\nsec-index\nsec-index"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Repeated Container Text Fixture</title>\n <style>\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #f5f5f4;\n color: #222222;\n }\n .card {\n background: #ffffff;\n border: 1px solid #d6d3d1;\n border-radius: 10px;\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n .shadow-card {\n background: #ffffff;\n border-radius: 10px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n h2 { font-size: 22px; margin: 0 0 10px; }\n .big-word { font-size: 30px; font-weight: 700; color: #b91c1c; }\n .tag { font-size: 12px; text-transform: uppercase; color: #b91c1c; }\n .row { padding: 6px 0; border-bottom: 1px solid #eeeeee; }\n .status { font-size: 13px; color: #57534e; }\n td, th { padding: 6px 10px; font-size: 14px; }\n </style>\n</head>\n<body>\n\n <!-- ══ SHOULD FLAG ══ -->\n\n <!-- Same word wired into three structurally different slots of one card -->\n <div class=\"card\" id=\"flag-status-card\">\n <h2>Museumplein departure</h2>\n <div class=\"headline\"><span class=\"big-word\">Suspended</span></div>\n <div class=\"meta\"><span class=\"tag\">Suspended</span></div>\n <p class=\"note\">This service is <strong>Suspended</strong> until further notice.</p>\n </div>\n\n <!-- Four distinct slots inside one elevated card -->\n <div class=\"shadow-card\" id=\"flag-ticket-card\">\n <h2>Evening show</h2>\n <div class=\"banner\"><span class=\"big-word\">Unavailable</span></div>\n <div class=\"detail\"><em class=\"tag\">Unavailable</em></div>\n <div class=\"footer\"><span class=\"status\">Unavailable</span></div>\n <p class=\"aside\">Currently <strong>Unavailable</strong> at this venue.</p>\n </div>\n\n <!-- ══ SHOULD PASS ══ -->\n\n <!-- Table columns inside a card repeat by design -->\n <div class=\"card\" id=\"pass-table-card\">\n <h2>Deploy history</h2>\n <table>\n <tr><td>build 12</td><td>Rolled back</td></tr>\n <tr><td>build 13</td><td>Rolled back</td></tr>\n <tr><td>build 14</td><td>Rolled back</td></tr>\n </table>\n </div>\n\n <!-- Parallel templated rows: identical structural position each time -->\n <div class=\"card\" id=\"pass-parallel-rows\">\n <h2>Departures</h2>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n <div class=\"row\"><span class=\"status\">On schedule</span></div>\n </div>\n\n <!-- Nav list inside a card -->\n <div class=\"card\" id=\"pass-nav-card\">\n <h2>Sections</h2>\n <nav>\n <div class=\"a\"><span>Overview page</span></div>\n <div class=\"b\"><em>Overview page</em></div>\n <div class=\"c\"><strong>Overview page</strong></div>\n </nav>\n </div>\n\n <!-- Short strings never count -->\n <div class=\"card\" id=\"pass-short-text\">\n <h2>Toggles</h2>\n <div class=\"one\"><span>Off</span></div>\n <div class=\"two\"><em>Off</em></div>\n <div class=\"three\"><strong>Off</strong></div>\n </div>\n\n <!-- Digits-only strings never count -->\n <div class=\"card\" id=\"pass-numbers\">\n <h2>Scores</h2>\n <div class=\"one\"><span>2026</span></div>\n <div class=\"two\"><em>2026</em></div>\n <div class=\"three\"><strong>2026</strong></div>\n </div>\n\n <!-- One occurrence per card, three cards -->\n <div class=\"card\" id=\"pass-spread-a\"><span class=\"tag\">Standby mode</span></div>\n <div class=\"card\" id=\"pass-spread-b\"><em class=\"tag\">Standby mode</em></div>\n <div class=\"card\" id=\"pass-spread-c\"><strong class=\"tag\">Standby mode</strong></div>\n\n <!-- Calendar-style grid repeats by design -->\n <div class=\"card\" id=\"pass-grid-card\">\n <h2>Availability</h2>\n <div role=\"grid\">\n <div class=\"cell-a\"><span>Open slot</span></div>\n <div class=\"cell-b\"><em>Open slot</em></div>\n <div class=\"cell-c\"><strong>Open slot</strong></div>\n </div>\n </div>\n\n <!-- Only two structurally distinct occurrences: below threshold -->\n <div class=\"card\" id=\"pass-two-spots\">\n <h2>Corner case</h2>\n <div class=\"headline\"><span class=\"big-word\">Rescheduled</span></div>\n <div class=\"meta\"><span class=\"tag\">Rescheduled</span></div>\n </div>\n\n</body>\n</html>\n",{"styleText":"\n body {\n font-family: Georgia, serif;\n font-size: 16px;\n margin: 0;\n padding: 40px;\n background: #f5f5f4;\n color: #222222;\n }\n .card {\n background: #ffffff;\n border: 1px solid #d6d3d1;\n border-radius: 10px;\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n .shadow-card {\n background: #ffffff;\n border-radius: 10px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n padding: 20px;\n width: 360px;\n margin-bottom: 32px;\n }\n h2 { font-size: 22px; margin: 0 0 10px; }\n .big-word { font-size: 30px; font-weight: 700; color: #b91c1c; }\n .tag { font-size: 12px; text-transform: uppercase; color: #b91c1c; }\n .row { padding: 6px 0; border-bottom: 1px solid #eeeeee; }\n .status { font-size: 13px; color: #57534e; }\n td, th { padding: 6px 10px; font-size: 14px; }\n ","classText":"card\nheadline\nbig-word\nmeta\ntag\nnote\nshadow-card\nbanner\nbig-word\ndetail\ntag\nfooter\nstatus\naside\ncard\ncard\nrow\nstatus\nrow\nstatus\nrow\nstatus\ncard\na\nb\nc\ncard\none\ntwo\nthree\ncard\none\ntwo\nthree\ncard\ntag\ncard\ntag\ncard\ntag\ncard\ncell-a\ncell-b\ncell-c\ncard\nheadline\nbig-word\nmeta\ntag"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Icon-Tile-Stack — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h3> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }\n .case p { font-size: 14px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */\n .icon-pastel {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 2: solid-color icon-tile, white svg inside (no gradient) */\n .icon-solid {\n width: 64px; height: 64px;\n border-radius: 16px;\n background: #3b82f6;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 3: white-bg outlined icon-tile (no chromatic background at all) */\n .icon-white {\n width: 72px; height: 72px;\n border-radius: 18px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 4: larger square (still inside the 32128 range) */\n .icon-large {\n width: 96px; height: 96px;\n border-radius: 24px;\n background: #f1f5f9;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 5: inline emoji icon — tile contains an emoji/symbol character directly,\n not an <svg> or <i> child. This is the \"card-icon\" pattern from many\n generated demos. */\n .icon-emoji {\n width: 48px; height: 48px;\n border-radius: 12px;\n background: linear-gradient(135deg, #8b5cf6, #a855f7);\n display: flex; align-items: center; justify-content: center;\n font-size: 24px;\n margin-bottom: 16px;\n }\n\n /* ── PASS cases ── */\n\n /* a: round avatar above name (border-radius: 50% — circle, not rounded square) */\n .avatar-round {\n width: 64px; height: 64px;\n border-radius: 50%;\n background: #f59e0b;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* b: wide thumbnail above article title (16:9, much wider than tall) */\n .thumb-wide {\n width: 200px; height: 112px;\n border-radius: 8px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n\n /* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */\n .inline-row { display: flex; align-items: center; gap: 12px; }\n .inline-row .icon-inline {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: #ddd6fe;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n }\n\n /* d: nothing above the heading at all */\n .no-icon h3 { margin-top: 0; }\n\n /* e: icon-tile that's too small (under 32px) — out of size range */\n .icon-tiny {\n width: 24px; height: 24px;\n border-radius: 6px;\n background: #93c5fd;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* f: oversized image card / hero image (over 128px) — out of size range */\n .img-huge {\n width: 200px; height: 200px;\n border-radius: 16px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <div class=\"case\">\n <div class=\"icon-pastel\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3b82f6\" stroke-width=\"1.5\">\n <polyline points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n </div>\n <h3>Lightning Fast</h3>\n <p>Built for speed and performance from the ground up.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-solid\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ffffff\" stroke-width=\"1.5\">\n <path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/>\n </svg>\n </div>\n <h3>Secure Storage</h3>\n <p>Enterprise-grade security for your peace of mind.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-white\">\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0f172a\" stroke-width=\"1.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"M12 6v6l4 2\"/>\n </svg>\n </div>\n <h3>Easy Setup</h3>\n <p>Get started in minutes with zero configuration.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-large\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0f172a\" stroke-width=\"1.5\">\n <line x1=\"18\" y1=\"20\" x2=\"18\" y2=\"10\"/>\n <line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/>\n <line x1=\"6\" y1=\"20\" x2=\"6\" y2=\"14\"/>\n </svg>\n </div>\n <h3>Powerful Analytics</h3>\n <p>Insights that drive smarter business decisions.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"icon-emoji\">&#9889;</div>\n <h3>Emoji Inline Icon</h3>\n <p>The tile contains an emoji directly, no SVG child.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <!-- a: round avatar (circle, not rounded square) -->\n <div class=\"case\">\n <div class=\"avatar-round\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/>\n <path d=\"M4 22v-2a8 8 0 0116 0v2\"/>\n </svg>\n </div>\n <h3>Sarah Chen</h3>\n <p>Product designer based in Berlin.</p>\n </div>\n\n <!-- b: wide 16:9 thumbnail (aspect ratio out of range) -->\n <div class=\"case\">\n <div class=\"thumb-wide\"></div>\n <h3>Article Headline</h3>\n <p>A summary of an interesting article.</p>\n </div>\n\n <!-- c: side-by-side icon and heading (not stacked vertically) -->\n <div class=\"case\">\n <div class=\"inline-row\">\n <div class=\"icon-inline\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#7c3aed\" stroke-width=\"1.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n </svg>\n </div>\n <div>\n <h3>Inline Side By Side</h3>\n <p>Icon sits next to the heading, not above it.</p>\n </div>\n </div>\n </div>\n\n <!-- d: no icon above the heading at all -->\n <div class=\"case no-icon\">\n <h3>Plain Heading No Icon</h3>\n <p>Just a heading with body text, nothing above.</p>\n </div>\n\n <!-- e: icon-tile too small (24px — under 32px floor) -->\n <div class=\"case\">\n <div class=\"icon-tiny\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#1e40af\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n </svg>\n </div>\n <h3>Tiny Icon Above Me</h3>\n <p>The icon container is smaller than 32px.</p>\n </div>\n\n <!-- f: huge image / hero (200px — over 128px ceiling) -->\n <div class=\"case\">\n <div class=\"img-huge\"></div>\n <h3>Huge Hero Image</h3>\n <p>The container is larger than the size cap.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h3> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }\n .case p { font-size: 14px; margin: 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */\n .icon-pastel {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 2: solid-color icon-tile, white svg inside (no gradient) */\n .icon-solid {\n width: 64px; height: 64px;\n border-radius: 16px;\n background: #3b82f6;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 3: white-bg outlined icon-tile (no chromatic background at all) */\n .icon-white {\n width: 72px; height: 72px;\n border-radius: 18px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 4: larger square (still inside the 32128 range) */\n .icon-large {\n width: 96px; height: 96px;\n border-radius: 24px;\n background: #f1f5f9;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* 5: inline emoji icon — tile contains an emoji/symbol character directly,\n not an <svg> or <i> child. This is the \"card-icon\" pattern from many\n generated demos. */\n .icon-emoji {\n width: 48px; height: 48px;\n border-radius: 12px;\n background: linear-gradient(135deg, #8b5cf6, #a855f7);\n display: flex; align-items: center; justify-content: center;\n font-size: 24px;\n margin-bottom: 16px;\n }\n\n /* ── PASS cases ── */\n\n /* a: round avatar above name (border-radius: 50% — circle, not rounded square) */\n .avatar-round {\n width: 64px; height: 64px;\n border-radius: 50%;\n background: #f59e0b;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* b: wide thumbnail above article title (16:9, much wider than tall) */\n .thumb-wide {\n width: 200px; height: 112px;\n border-radius: 8px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n\n /* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */\n .inline-row { display: flex; align-items: center; gap: 12px; }\n .inline-row .icon-inline {\n width: 80px; height: 80px;\n border-radius: 20px;\n background: #ddd6fe;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n }\n\n /* d: nothing above the heading at all */\n .no-icon h3 { margin-top: 0; }\n\n /* e: icon-tile that's too small (under 32px) — out of size range */\n .icon-tiny {\n width: 24px; height: 24px;\n border-radius: 6px;\n background: #93c5fd;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 16px;\n }\n\n /* f: oversized image card / hero image (over 128px) — out of size range */\n .img-huge {\n width: 200px; height: 200px;\n border-radius: 16px;\n background: #cbd5e1;\n margin-bottom: 16px;\n }\n ","classText":"grid\ncol\ncase\nicon-pastel\ncase\nicon-solid\ncase\nicon-white\ncase\nicon-large\ncase\nicon-emoji\ncol\ncase\navatar-round\ncase\nthumb-wide\ncase\ninline-row\nicon-inline\ncase no-icon\ncase\nicon-tiny\ncase\nimg-huge"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":{"$undef":true}}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>radial-spotlight-glow fixture</title>\n<style>\n /* jsdom / static-html engine does no layout: every case declares explicit\n pixel width + height so the size gate can read them. */\n body { margin: 0; background: #0b0d13; }\n .grid { display: flex; flex-wrap: wrap; gap: 24px; padding: 24px; }\n .case { color: #e6e6e6; }\n\n /* ───────── SHOULD FLAG: decorative low-opacity chromatic spotlights ───────── */\n\n /* The exact declaration that evaded the detector: soft blue spotlight fading\n to transparent, painted on a large mobile hero. */\n .flag-hero-blue {\n width: 360px; height: 560px;\n background: radial-gradient(circle at 52% 38%, rgba(80,111,255,0.26), transparent 44%);\n }\n /* Violet accent wash on a full section. */\n .flag-section-violet {\n width: 900px; height: 600px;\n background-image: radial-gradient(circle, rgba(139,92,246,0.30), transparent 60%);\n }\n /* Cyan overlay-style glow (the pseudo-element overlay div reflex). */\n .flag-overlay-cyan {\n width: 760px; height: 480px;\n background: radial-gradient(ellipse at top, rgba(34,211,238,0.22), transparent 70%);\n }\n /* Single glow drawn with two low-alpha stops of one hue fading out. */\n .flag-two-stop-rose {\n width: 640px; height: 400px;\n background: radial-gradient(circle, rgba(255,90,120,0.28) 0%, rgba(255,90,120,0.12) 45%, transparent 75%);\n }\n /* Same tell written as an 8-digit hex (#506fff at alpha 0x3d ≈ 0.24). */\n .flag-hex-alpha {\n width: 700px; height: 460px;\n background: radial-gradient(circle, #506fff3d, transparent 50%);\n }\n\n /* ───────── SHOULD PASS: real backgrounds, small elements, neutrals, rich art ───────── */\n\n /* Opaque radial background — a real surface, not a glow (no transparent end). */\n .pass-opaque-bg {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #1e3a8a, #0f172a);\n }\n /* Small badge: chromatic low-alpha radial glow, but tiny. */\n .pass-badge {\n width: 40px; height: 40px;\n background: radial-gradient(circle, rgba(80,111,255,0.30), transparent);\n }\n /* Avatar-scale light: the subject IS a small light, exempt by size. */\n .pass-avatar-light {\n width: 72px; height: 72px;\n background: radial-gradient(circle, rgba(255,196,64,0.35), transparent 70%);\n }\n /* Neutral near-black vignette — grayscale, not the accent-glow tell. */\n .pass-neutral-vignette {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(0,0,0,0.40), transparent 80%);\n }\n /* Neutral near-white vignette — grayscale, exempt. */\n .pass-white-vignette {\n width: 820px; height: 540px;\n background: radial-gradient(ellipse, rgba(255,255,255,0.35), transparent 70%);\n }\n /* Rich three-color radial composition — real color structure, opaque, no fade. */\n .pass-rich-composition {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #ff0080 0%, #7928ca 45%, #0070f3 100%);\n }\n /* Rich composition that does fade out, but its color stops are opaque. */\n .pass-rich-transparent {\n width: 780px; height: 500px;\n background: radial-gradient(circle, rgba(255,0,128,0.90) 0%, rgba(121,40,202,0.85) 50%, transparent);\n }\n /* Opaque center glow (the radial-halo case, alpha ≥ 0.45) — not this rule. */\n .pass-opaque-center {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(80,111,255,0.85), transparent);\n }\n /* Low-alpha chromatic wash, but LINEAR, not radial. */\n .pass-linear-wash {\n width: 800px; height: 520px;\n background: linear-gradient(120deg, rgba(80,111,255,0.28), transparent 60%);\n }\n</style>\n</head>\n<body>\n <main class=\"grid\">\n <!-- should-flag column -->\n <div class=\"case flag-hero-blue\" data-name=\"Hero Spotlight Blue\"></div>\n <div class=\"case flag-section-violet\" data-name=\"Section Glow Violet\"></div>\n <div class=\"case flag-overlay-cyan\" data-name=\"Overlay Glow Cyan\"></div>\n <div class=\"case flag-two-stop-rose\" data-name=\"Two Stop Soft Glow\"></div>\n <div class=\"case flag-hex-alpha\" data-name=\"Hex Alpha Glow\"></div>\n\n <!-- should-pass column -->\n <div class=\"case pass-opaque-bg\" data-name=\"Opaque Radial Background\"></div>\n <div class=\"case pass-badge\" data-name=\"Small Accent Badge\"></div>\n <div class=\"case pass-avatar-light\" data-name=\"Avatar Glow Light\"></div>\n <div class=\"case pass-neutral-vignette\" data-name=\"Neutral Vignette\"></div>\n <div class=\"case pass-white-vignette\" data-name=\"White Vignette\"></div>\n <div class=\"case pass-rich-composition\" data-name=\"Rich Radial Composition\"></div>\n <div class=\"case pass-rich-transparent\" data-name=\"Rich Transparent Composition\"></div>\n <div class=\"case pass-opaque-center\" data-name=\"Opaque Center Glow\"></div>\n <div class=\"case pass-linear-wash\" data-name=\"Linear Gradient Wash\"></div>\n </main>\n</body>\n</html>\n",{"styleText":"\n /* jsdom / static-html engine does no layout: every case declares explicit\n pixel width + height so the size gate can read them. */\n body { margin: 0; background: #0b0d13; }\n .grid { display: flex; flex-wrap: wrap; gap: 24px; padding: 24px; }\n .case { color: #e6e6e6; }\n\n /* ───────── SHOULD FLAG: decorative low-opacity chromatic spotlights ───────── */\n\n /* The exact declaration that evaded the detector: soft blue spotlight fading\n to transparent, painted on a large mobile hero. */\n .flag-hero-blue {\n width: 360px; height: 560px;\n background: radial-gradient(circle at 52% 38%, rgba(80,111,255,0.26), transparent 44%);\n }\n /* Violet accent wash on a full section. */\n .flag-section-violet {\n width: 900px; height: 600px;\n background-image: radial-gradient(circle, rgba(139,92,246,0.30), transparent 60%);\n }\n /* Cyan overlay-style glow (the pseudo-element overlay div reflex). */\n .flag-overlay-cyan {\n width: 760px; height: 480px;\n background: radial-gradient(ellipse at top, rgba(34,211,238,0.22), transparent 70%);\n }\n /* Single glow drawn with two low-alpha stops of one hue fading out. */\n .flag-two-stop-rose {\n width: 640px; height: 400px;\n background: radial-gradient(circle, rgba(255,90,120,0.28) 0%, rgba(255,90,120,0.12) 45%, transparent 75%);\n }\n /* Same tell written as an 8-digit hex (#506fff at alpha 0x3d ≈ 0.24). */\n .flag-hex-alpha {\n width: 700px; height: 460px;\n background: radial-gradient(circle, #506fff3d, transparent 50%);\n }\n\n /* ───────── SHOULD PASS: real backgrounds, small elements, neutrals, rich art ───────── */\n\n /* Opaque radial background — a real surface, not a glow (no transparent end). */\n .pass-opaque-bg {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #1e3a8a, #0f172a);\n }\n /* Small badge: chromatic low-alpha radial glow, but tiny. */\n .pass-badge {\n width: 40px; height: 40px;\n background: radial-gradient(circle, rgba(80,111,255,0.30), transparent);\n }\n /* Avatar-scale light: the subject IS a small light, exempt by size. */\n .pass-avatar-light {\n width: 72px; height: 72px;\n background: radial-gradient(circle, rgba(255,196,64,0.35), transparent 70%);\n }\n /* Neutral near-black vignette — grayscale, not the accent-glow tell. */\n .pass-neutral-vignette {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(0,0,0,0.40), transparent 80%);\n }\n /* Neutral near-white vignette — grayscale, exempt. */\n .pass-white-vignette {\n width: 820px; height: 540px;\n background: radial-gradient(ellipse, rgba(255,255,255,0.35), transparent 70%);\n }\n /* Rich three-color radial composition — real color structure, opaque, no fade. */\n .pass-rich-composition {\n width: 800px; height: 520px;\n background: radial-gradient(circle, #ff0080 0%, #7928ca 45%, #0070f3 100%);\n }\n /* Rich composition that does fade out, but its color stops are opaque. */\n .pass-rich-transparent {\n width: 780px; height: 500px;\n background: radial-gradient(circle, rgba(255,0,128,0.90) 0%, rgba(121,40,202,0.85) 50%, transparent);\n }\n /* Opaque center glow (the radial-halo case, alpha ≥ 0.45) — not this rule. */\n .pass-opaque-center {\n width: 800px; height: 520px;\n background: radial-gradient(circle, rgba(80,111,255,0.85), transparent);\n }\n /* Low-alpha chromatic wash, but LINEAR, not radial. */\n .pass-linear-wash {\n width: 800px; height: 520px;\n background: linear-gradient(120deg, rgba(80,111,255,0.28), transparent 60%);\n }\n","classText":"grid\ncase flag-hero-blue\ncase flag-section-violet\ncase flag-overlay-cyan\ncase flag-two-stop-rose\ncase flag-hex-alpha\ncase pass-opaque-bg\ncase pass-badge\ncase pass-avatar-light\ncase pass-neutral-vignette\ncase pass-white-vignette\ncase pass-rich-composition\ncase pass-rich-transparent\ncase pass-opaque-center\ncase pass-linear-wash"}],"result":[{"id":"radial-halo","snippet":"radial-gradient halo (#ff0080 → transparent) on dark page","selector":"/* Rich composition that does fade out, but its color stops are opaque. */ .pass-rich-transparent"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>undersized-ui-text fixture</title>\n<style>\n /* Explicit pixel dimensions because jsdom does no layout. */\n body { font-family: system-ui, sans-serif; font-size: 16px; }\n .col { display: inline-block; width: 480px; vertical-align: top; }\n\n /* --- should-flag styles --- */\n .nav-link { font-size: 8px; }\n .category { font-size: 8px; }\n .meta { font-size: 9px; }\n .btn-small { font-size: 10px; width: 120px; height: 24px; }\n .cell-small { font-size: 9px; }\n .caps-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; }\n .footer-link { font-size: 8px; }\n\n /* --- should-pass styles --- */\n .legal { font-size: 10px; line-height: 1.5; } /* non-interactive footer smallprint at 10px */\n .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); font-size: 4px; }\n .sup-marker { font-size: 8px; }\n .sub-marker { font-size: 8px; }\n .em-parent { font-size: 20px; }\n .em-child { font-size: 0.6em; } /* resolves to 12px, above the floor */\n .mock-terminal { width: 400px; }\n .term-line { font-size: 9px; } /* legitimately small code/terminal text */\n .normal-link { font-size: 12px; } /* at the floor, allowed */\n</style>\n</head>\n<body>\n\n<!-- ================= SHOULD FLAG (functional/UI text below the 11px floor) ================= -->\n<div class=\"col\" id=\"should-flag\">\n <h2>Should flag</h2>\n\n <!-- interactive: nav link at 8px -->\n <nav aria-label=\"primary\">\n <a href=\"/docs\" class=\"nav-link\">Flag Nav Link</a>\n </nav>\n\n <!-- non-interactive furniture: category label at 8px -->\n <span class=\"category\">Flag Category</span>\n\n <!-- non-interactive furniture: meta row at 9px -->\n <span class=\"meta\">Flag Meta Row</span>\n\n <!-- interactive: button label at 10px -->\n <button type=\"button\" class=\"btn-small\">Flag Button</button>\n\n <!-- structural furniture: table cell at 9px -->\n <table>\n <tbody>\n <tr><td class=\"cell-small\">Flag Table Cell</td></tr>\n </tbody>\n </table>\n\n <!-- decorative letterspaced uppercase micro-label at 10px: STILL functional, not exempt -->\n <span class=\"caps-label\">Flag Caps Label</span>\n\n <!-- interactive text in a footer stays on the 11px floor: footer link at 8px -->\n <footer>\n <a href=\"/privacy\" class=\"footer-link\">Flag Footer Link</a>\n </footer>\n</div>\n\n<!-- ================= SHOULD PASS ================= -->\n<div class=\"col\" id=\"should-pass\">\n <h2>Should pass</h2>\n\n <!-- non-interactive legal fine print in a footer at 10px: floor drops to 10px for smallprint -->\n <footer>\n <p class=\"legal\">Pass Legal Fine Print copyright 2026 all rights reserved across this jurisdiction</p>\n </footer>\n\n <!-- visually-hidden / screen-reader text: never rendered, exempt -->\n <span class=\"sr-only\">Pass Sr Only</span>\n\n <!-- sup / sub markers are exempt by tag -->\n <p>Reference<sup class=\"sup-marker\">Pass Sup Marker</sup> and water<sub class=\"sub-marker\">Pass Sub Marker</sub></p>\n\n <!-- em-sized text relative to a large parent computes to 12px, above the floor -->\n <div class=\"em-parent\"><span class=\"em-child\">Pass Em Sized</span></div>\n\n <!-- code / terminal mock: legitimately small, exempt -->\n <div class=\"mock-terminal\">\n <span class=\"term-line\">Pass Terminal Line</span>\n </div>\n\n <!-- functional text exactly at the 12px floor is allowed -->\n <a href=\"/home\" class=\"normal-link\">Pass Normal Link</a>\n</div>\n\n</body>\n</html>\n",{"styleText":"\n /* Explicit pixel dimensions because jsdom does no layout. */\n body { font-family: system-ui, sans-serif; font-size: 16px; }\n .col { display: inline-block; width: 480px; vertical-align: top; }\n\n /* --- should-flag styles --- */\n .nav-link { font-size: 8px; }\n .category { font-size: 8px; }\n .meta { font-size: 9px; }\n .btn-small { font-size: 10px; width: 120px; height: 24px; }\n .cell-small { font-size: 9px; }\n .caps-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; }\n .footer-link { font-size: 8px; }\n\n /* --- should-pass styles --- */\n .legal { font-size: 10px; line-height: 1.5; } /* non-interactive footer smallprint at 10px */\n .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); font-size: 4px; }\n .sup-marker { font-size: 8px; }\n .sub-marker { font-size: 8px; }\n .em-parent { font-size: 20px; }\n .em-child { font-size: 0.6em; } /* resolves to 12px, above the floor */\n .mock-terminal { width: 400px; }\n .term-line { font-size: 9px; } /* legitimately small code/terminal text */\n .normal-link { font-size: 12px; } /* at the floor, allowed */\n","classText":"col\nnav-link\ncategory\nmeta\nbtn-small\ncell-small\ncaps-label\nfooter-link\ncol\nlegal\nsr-only\nsup-marker\nsub-marker\nem-parent\nem-child\nmock-terminal\nterm-line\nnormal-link"}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Regression fixture for issue #408: tiny-text / undersized-ui-text must only\n measure rendered text. On sites that set `html { font-size: 62.5% }` the root\n computes to 10px, so every element that inherits the root and carries text\n content — including <script>, <style>, <title>, <noscript>, and display:none /\n visibility:hidden blocks — reports a 10px computed size. Their JS / CSS /\n JSON-LD text satisfies hasDirectText, so before the fix they produced dozens\n of phantom \"10px body text\" findings on every Shopify page.\n\n Explicit pixel sizes throughout because jsdom does no layout.\n-->\n<html style=\"font-size: 10px\">\n<head>\n<meta charset=\"utf-8\">\n<!-- SHOULD PASS: a long <title> inherits the 10px root, but nothing renders it. -->\n<title>This is a fairly long document title that easily exceeds twenty characters</title>\n<!-- SHOULD PASS: stylesheet text is never painted. -->\n<style>\n /* This CSS comment is deliberately longer than twenty characters so the style\n element's text node clears the hasDirectText / textLen > 20 gate. */\n body { font-size: 10px; font-family: system-ui, sans-serif; }\n .rendered-body { font-size: 10px; }\n .rendered-nav-link { font-size: 9px; }\n .none-block { display: none; font-size: 10px; }\n .hidden-block { visibility: hidden; font-size: 10px; }\n</style>\n<!-- SHOULD PASS: script payload text is never painted. -->\n<script>window.__ANALYTICS__ = { id: 1, ts: 0 }; console.log(\"an analytics payload string that is clearly longer than twenty characters\");</script>\n<!-- SHOULD PASS: JSON-LD schema block, a Shopify staple. -->\n<script type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"Product\",\"name\":\"A product name long enough to exceed twenty characters\"}</script>\n<!-- SHOULD PASS: noscript fallback prose is only shown without JS; still non-rendered here. -->\n<noscript>Please enable JavaScript in your browser to view this page content correctly.</noscript>\n</head>\n<body>\n\n<!-- SHOULD PASS: display:none block, its text is never painted. -->\n<div class=\"none-block\">Hidden display-none block of body text long enough to exceed twenty characters.</div>\n\n<!-- SHOULD PASS: visibility:hidden paragraph, its text is never painted. -->\n<p class=\"hidden-block\">Invisible visibility-hidden paragraph copy that is longer than the twenty char gate.</p>\n\n<!-- SHOULD FLAG (tiny-text): genuinely rendered body copy at 10px. -->\n<p class=\"rendered-body\">This is real rendered body copy at 10px that is definitely long enough to flag.</p>\n\n<!-- SHOULD FLAG (undersized-ui-text): genuinely rendered interactive text at 9px. -->\n<nav aria-label=\"primary\"><a href=\"/docs\" class=\"rendered-nav-link\">Rendered Nav Link</a></nav>\n\n</body>\n</html>\n",{"styleText":"\n /* This CSS comment is deliberately longer than twenty characters so the style\n element's text node clears the hasDirectText / textLen > 20 gate. */\n body { font-size: 10px; font-family: system-ui, sans-serif; }\n .rendered-body { font-size: 10px; }\n .rendered-nav-link { font-size: 9px; }\n .none-block { display: none; font-size: 10px; }\n .hidden-block { visibility: hidden; font-size: 10px; }\n\nstyle=\"font-size: 10px\"","classText":"none-block\nhidden-block\nrendered-body\nrendered-nav-link"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Quality (Typography & Readability) — Should Flag vs Should Pass</title>\n <style>\n /* Two-column fixture: left = should flag, right = should pass.\n Covers the typography-quality rules that need real browser layout\n (line-length, tight-leading, tiny-text, justified-text, all-caps-body,\n wide-tracking, skipped-heading). All are browser-only — see\n tests/detect-antipatterns-browser.test.mjs for assertions. */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.6; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .case { margin-bottom: 16px; padding: 16px; background: white; border: 1px solid #e2e8f0; border-radius: 8px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 6px; font-style: italic; }\n\n /* ── FLAG: typography quality issues ── */\n\n /* Line length: 12px body text fits >85 chars/line in a ~528px column.\n 12px is not below the tiny-text 12px floor (rule fires at <12px). */\n .long-lines {\n font-size: 12px;\n line-height: 1.6;\n }\n\n /* Tight leading */\n .tight-leading {\n font-size: 16px;\n line-height: 1.0;\n max-width: 40em;\n }\n\n /* Tiny body text */\n .tiny-text {\n font-size: 10px;\n line-height: 1.6;\n max-width: 40em;\n }\n .tiny-text-11 {\n font-size: 11px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text without hyphens */\n .justified-no-hyphens {\n text-align: justify;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* All-caps long body */\n .all-caps-body {\n text-transform: uppercase;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* Wide letter spacing on body */\n .wide-tracking-body {\n letter-spacing: 0.15em;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* ── PASS: comfortable typography ── */\n\n .good-measure {\n max-width: 65ch;\n font-size: 16px;\n line-height: 1.6;\n }\n\n .good-leading {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n .good-text-size {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text WITH hyphens — fine */\n .justified-with-hyphens {\n text-align: justify;\n hyphens: auto;\n -webkit-hyphens: auto;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* Short label in all-caps (passes — under 30 chars) */\n .label-caps {\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.1em;\n color: #475569;\n }\n\n /* Wide tracking on a short uppercase label (passes — both conditions excluded) */\n .label-tracking {\n letter-spacing: 0.15em;\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n }\n\n .mock-terminal-title,\n .mock-terminal-meta,\n .mock-diff-line {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: #475569;\n line-height: 1.5;\n }\n .mock-terminal-title { font-size: 11px; }\n .mock-terminal-meta { font-size: 10.8px; }\n .mock-diff-line { font-size: 11.84px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Line length too long</h3>\n <div class=\"case\">\n <span class=\"case-label\">no max-width on a paragraph</span>\n <p class=\"long-lines\">This paragraph has no max-width constraint at all, which means on a wide monitor or ultrawide display, each line of text can stretch to 150 or even 200 characters wide. Research consistently shows that line lengths beyond 75 characters significantly reduce reading speed and comprehension. The eye has to travel too far to find the beginning of the next line, causing readers to lose their place. A simple max-width of 65ch to 75ch on the paragraph or its container would fix this entirely.</p>\n </div>\n\n <h3>Tight line height</h3>\n <div class=\"case\">\n <span class=\"case-label\">line-height: 1.0</span>\n <p class=\"tight-leading\">This paragraph has a line-height of 1.0, which means the lines are touching. Multi-line body text needs breathing room between lines for readability. A line-height of 1.5 to 1.7 is generally recommended for body text.</p>\n </div>\n\n <h3>Tiny body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">10px body text</span>\n <p class=\"tiny-text\">This body text is only 10px. While this might be fine for a disclaimer or legal footnote, it's too small for primary content that users need to actually read. Aim for at least 14px for body content, 16px is ideal.</p>\n </div>\n\n <h3>Justified text without hyphens</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-align: justify, no hyphens: auto</span>\n <p class=\"justified-no-hyphens\">This paragraph uses text-align: justify, which forces each line to stretch to fill the full width. Without hyphenation support, this creates uneven gaps between words known as \"rivers of white space\" that flow vertically through the text. Left-aligned text is almost always more readable on the web.</p>\n </div>\n\n <h3>All-caps body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-transform: uppercase on a long passage</span>\n <p class=\"all-caps-body\">This entire paragraph is in uppercase via text-transform. While all-caps works for short labels, headings, or navigation items, longer body text in uppercase is significantly harder to read because we lose the word shape cues that come from ascenders and descenders in mixed-case text.</p>\n </div>\n\n <h3>Wide letter spacing on body text</h3>\n <div class=\"case\">\n <span class=\"case-label\">letter-spacing: 0.15em on body text</span>\n <p class=\"wide-tracking-body\">This body text has letter-spacing: 0.15em applied to it. While subtle tracking adjustments can improve readability for headings or all-caps text, adding significant letter spacing to body text actually makes it harder to read by disrupting natural character groupings.</p>\n </div>\n\n <h3>Skipped heading levels</h3>\n <div class=\"case\">\n <span class=\"case-label\">h1 → h3 (missing h2)</span>\n <h1 style=\"font-size: 20px; margin: 0 0 4px;\">Top heading</h1>\n <h3 style=\"font-size: 16px; margin: 0;\">Skips straight to h3</h3>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Comfortable line length</h3>\n <div class=\"case\">\n <span class=\"case-label\">max-width: 65ch on the paragraph</span>\n <p class=\"good-measure\">This paragraph has a max-width of 65ch, keeping the line length comfortable for reading. The eye can easily track from the end of one line to the beginning of the next without losing its place.</p>\n </div>\n\n <h3>Comfortable line height</h3>\n <div class=\"case\">\n <span class=\"case-label\">line-height: 1.6</span>\n <p class=\"good-leading\">This paragraph has a line-height of 1.6, which gives multi-line text plenty of room to breathe and improves the rhythm of the page.</p>\n </div>\n\n <h3>Comfortable body text size</h3>\n <div class=\"case\">\n <span class=\"case-label\">16px body text</span>\n <p class=\"good-text-size\">This is 16px body text — the recommended baseline for comfortable reading on modern displays.</p>\n </div>\n\n <h3>Justified text with hyphens</h3>\n <div class=\"case\">\n <span class=\"case-label\">text-align: justify + hyphens: auto</span>\n <p class=\"justified-with-hyphens\">When justified text is paired with hyphens: auto, the browser can break long words across lines, eliminating the rivers of white space and making the justification look intentional rather than awkward.</p>\n </div>\n\n <h3>Short label in all-caps</h3>\n <div class=\"case\">\n <span class=\"case-label\">short label, uppercase</span>\n <span class=\"label-caps\">Featured</span>\n </div>\n\n <h3>Wide tracking on a short uppercase label</h3>\n <div class=\"case\">\n <span class=\"case-label\">letter-spacing on a short uppercase label</span>\n <span class=\"label-tracking\">Beta</span>\n </div>\n\n <h3>Proper heading hierarchy</h3>\n <div class=\"case\">\n <span class=\"case-label\">h1 → h2 → h3</span>\n <h1 style=\"font-size: 20px; margin: 0 0 4px;\">Top heading</h1>\n <h2 style=\"font-size: 18px; margin: 0 0 4px;\">Second level</h2>\n <h3 style=\"font-size: 16px; margin: 0;\">Third level</h3>\n </div>\n\n <h3>Small mockup metadata</h3>\n <div class=\"case\">\n <span class=\"case-label\">terminal/diff metadata at tiny sizes</span>\n <span class=\"mock-terminal-title\">/impeccable polish : scanning codebase</span>\n <div class=\"mock-terminal-meta\">12 files inspected · 4 visual issues queued</div>\n <div class=\"mock-diff-line\">+ Replace generic button label with action-specific copy</div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n <!-- SVG text: Chrome reports fake scrollWidth/clientWidth on SVG content;\n the text-overflow rule must skip it (pass). -->\n <svg class=\"svg-metrics-pass\" viewBox=\"0 0 430 60\" width=\"430\" height=\"60\" role=\"img\" aria-label=\"diagram\">\n <rect x=\"6\" y=\"10\" width=\"96\" height=\"40\" rx=\"3\" fill=\"none\" stroke=\"#888\"/>\n <text x=\"54\" y=\"35\" text-anchor=\"middle\" font-size=\"13\">impeccable</text>\n </svg>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left = should flag, right = should pass.\n Covers the typography-quality rules that need real browser layout\n (line-length, tight-leading, tiny-text, justified-text, all-caps-body,\n wide-tracking, skipped-heading). All are browser-only — see\n tests/detect-antipatterns-browser.test.mjs for assertions. */\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.6; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .case { margin-bottom: 16px; padding: 16px; background: white; border: 1px solid #e2e8f0; border-radius: 8px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 6px; font-style: italic; }\n\n /* ── FLAG: typography quality issues ── */\n\n /* Line length: 12px body text fits >85 chars/line in a ~528px column.\n 12px is not below the tiny-text 12px floor (rule fires at <12px). */\n .long-lines {\n font-size: 12px;\n line-height: 1.6;\n }\n\n /* Tight leading */\n .tight-leading {\n font-size: 16px;\n line-height: 1.0;\n max-width: 40em;\n }\n\n /* Tiny body text */\n .tiny-text {\n font-size: 10px;\n line-height: 1.6;\n max-width: 40em;\n }\n .tiny-text-11 {\n font-size: 11px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text without hyphens */\n .justified-no-hyphens {\n text-align: justify;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* All-caps long body */\n .all-caps-body {\n text-transform: uppercase;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* Wide letter spacing on body */\n .wide-tracking-body {\n letter-spacing: 0.15em;\n font-size: 16px;\n max-width: 40em;\n line-height: 1.6;\n }\n\n /* ── PASS: comfortable typography ── */\n\n .good-measure {\n max-width: 65ch;\n font-size: 16px;\n line-height: 1.6;\n }\n\n .good-leading {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n .good-text-size {\n font-size: 16px;\n line-height: 1.6;\n max-width: 40em;\n }\n\n /* Justified text WITH hyphens — fine */\n .justified-with-hyphens {\n text-align: justify;\n hyphens: auto;\n -webkit-hyphens: auto;\n max-width: 30em;\n font-size: 16px;\n line-height: 1.6;\n }\n\n /* Short label in all-caps (passes — under 30 chars) */\n .label-caps {\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.1em;\n color: #475569;\n }\n\n /* Wide tracking on a short uppercase label (passes — both conditions excluded) */\n .label-tracking {\n letter-spacing: 0.15em;\n text-transform: uppercase;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n }\n\n .mock-terminal-title,\n .mock-terminal-meta,\n .mock-diff-line {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: #475569;\n line-height: 1.5;\n }\n .mock-terminal-title { font-size: 11px; }\n .mock-terminal-meta { font-size: 10.8px; }\n .mock-diff-line { font-size: 11.84px; }\n \nstyle=\"font-size: 20px; margin: 0 0 4px;\"\nstyle=\"font-size: 16px; margin: 0;\"\nstyle=\"font-size: 20px; margin: 0 0 4px;\"\nstyle=\"font-size: 18px; margin: 0 0 4px;\"\nstyle=\"font-size: 16px; margin: 0;\"","classText":"grid\ncol\ncase\ncase-label\nlong-lines\ncase\ncase-label\ntight-leading\ncase\ncase-label\ntiny-text\ncase\ncase-label\njustified-no-hyphens\ncase\ncase-label\nall-caps-body\ncase\ncase-label\nwide-tracking-body\ncase\ncase-label\ncol\ncase\ncase-label\ngood-measure\ncase\ncase-label\ngood-leading\ncase\ncase-label\ngood-text-size\ncase\ncase-label\njustified-with-hyphens\ncase\ncase-label\nlabel-caps\ncase\ncase-label\nlabel-tracking\ncase\ncase-label\ncase\ncase-label\nmock-terminal-title\nmock-terminal-meta\nmock-diff-line\nsvg-metrics-pass"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Layout Anti-Patterns — Should Flag vs Should Pass</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; color: #111827; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Nested cards (cardocalypse)</h3>\n\n <!-- Classic: card inside card -->\n <div class=\"bg-white rounded-lg shadow-md p-6 mb-4\">\n <h4 class=\"text-lg font-semibold mb-3\">Outer Card</h4>\n <div class=\"bg-white rounded-lg shadow-md p-4\">\n <h5 class=\"font-medium\">Inner Card</h5>\n <p class=\"text-sm text-gray-600\">Card inside card.</p>\n </div>\n </div>\n\n <!-- Three levels deep -->\n <div class=\"bg-white rounded-xl shadow-lg p-6 mb-4\">\n <h4 class=\"text-lg font-semibold mb-3\">Level 1</h4>\n <div class=\"bg-gray-50 rounded-lg shadow-sm p-4 mb-3\">\n <h5 class=\"font-medium mb-2\">Level 2</h5>\n <div class=\"bg-white rounded-md shadow-sm p-3\">\n <p class=\"text-sm\">Level 3 — nesting inception.</p>\n </div>\n </div>\n </div>\n\n <!-- CSS variant: border + bg + shadow nesting -->\n <div style=\"background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 1.5rem; margin-bottom: 1rem;\">\n <h4 style=\"font-weight: 600; margin-bottom: 0.75rem;\">Outer (CSS)</h4>\n <div style=\"background: #f9fafb; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 1rem;\">\n <p style=\"font-size: 0.875rem; color: #6b7280;\">Inner card via CSS.</p>\n </div>\n </div>\n\n <!-- shadcn-style Card nested in Card -->\n <div class=\"rounded-lg border bg-white shadow-sm p-6 mb-4\" data-component=\"card\">\n <h4 class=\"font-semibold mb-3\">shadcn-style Outer Card</h4>\n <div class=\"rounded-lg border bg-white shadow-sm p-4\" data-component=\"card\">\n <p class=\"text-sm text-gray-600\">Another shadcn Card nested inside.</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>shadcn Card sub-components (not nested cards)</h3>\n <div class=\"rounded-lg border bg-white shadow-sm mb-4\">\n <div class=\"flex flex-col space-y-1.5 p-6\">\n <h4 class=\"text-lg font-semibold\">Card Title</h4>\n <p class=\"text-sm text-gray-500\">Card description goes here.</p>\n </div>\n <div class=\"p-6 pt-0\">\n <p class=\"text-sm text-gray-600\">CardContent — a sub-section, not a nested card. No shadow, no border-radius of its own.</p>\n </div>\n <div class=\"flex items-center p-6 pt-0\">\n <button class=\"px-4 py-2 bg-gray-900 text-white text-sm rounded-md\">Action</button>\n </div>\n </div>\n\n <h3>Card with form inputs</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-4\">Settings</h4>\n <div class=\"space-y-3\">\n <input type=\"text\" placeholder=\"Name\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm\">\n <input type=\"email\" placeholder=\"Email\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm\">\n <select class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white\">\n <option>Select option</option>\n </select>\n <textarea placeholder=\"Bio\" class=\"w-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none\"></textarea>\n </div>\n </div>\n\n <h3>Card with code block</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-3\">Installation</h4>\n <pre class=\"bg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto\"><code>npm install @acme/sdk</code></pre>\n </div>\n\n <h3>Card with badges</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <h4 class=\"font-semibold mb-3\">Tags</h4>\n <div class=\"flex flex-wrap gap-2\">\n <span class=\"px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium\">React</span>\n <span class=\"px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium\">TypeScript</span>\n <span class=\"px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium\">Tailwind</span>\n </div>\n </div>\n\n <h3>Card with image</h3>\n <div class=\"bg-white rounded-lg shadow-sm p-6 mb-4 border\">\n <div class=\"w-full h-32 bg-gradient-to-br from-amber-400 to-rose-500 rounded-lg shadow-inner mb-4\"></div>\n <h4 class=\"font-semibold\">Project Preview</h4>\n <p class=\"text-sm text-gray-600 mt-1\">Image with rounded corners and shadow.</p>\n </div>\n\n <h3>Pricing cards (intentionally similar)</h3>\n <div class=\"grid grid-cols-3 gap-2 mb-4\">\n <div class=\"bg-white rounded-lg shadow-sm p-4 border\">\n <h4 class=\"font-semibold mb-1 text-sm\">Free</h4>\n <div class=\"text-lg font-bold mb-2\">$0</div>\n </div>\n <div class=\"bg-white rounded-lg shadow-sm p-4 border-2 border-blue-500\">\n <h4 class=\"font-semibold mb-1 text-sm\">Pro</h4>\n <div class=\"text-lg font-bold mb-2\">$29</div>\n </div>\n <div class=\"bg-white rounded-lg shadow-sm p-4 border\">\n <h4 class=\"font-semibold mb-1 text-sm\">Enterprise</h4>\n <div class=\"text-lg font-bold mb-2\">Custom</div>\n </div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; color: #111827; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n \nstyle=\"background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 1.5rem; margin-bottom: 1rem;\"\nstyle=\"font-weight: 600; margin-bottom: 0.75rem;\"\nstyle=\"background: #f9fafb; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 1rem;\"\nstyle=\"font-size: 0.875rem; color: #6b7280;\"","classText":"grid\ncol\nbg-white rounded-lg shadow-md p-6 mb-4\ntext-lg font-semibold mb-3\nbg-white rounded-lg shadow-md p-4\nfont-medium\ntext-sm text-gray-600\nbg-white rounded-xl shadow-lg p-6 mb-4\ntext-lg font-semibold mb-3\nbg-gray-50 rounded-lg shadow-sm p-4 mb-3\nfont-medium mb-2\nbg-white rounded-md shadow-sm p-3\ntext-sm\nrounded-lg border bg-white shadow-sm p-6 mb-4\nfont-semibold mb-3\nrounded-lg border bg-white shadow-sm p-4\ntext-sm text-gray-600\ncol\nrounded-lg border bg-white shadow-sm mb-4\nflex flex-col space-y-1.5 p-6\ntext-lg font-semibold\ntext-sm text-gray-500\np-6 pt-0\ntext-sm text-gray-600\nflex items-center p-6 pt-0\npx-4 py-2 bg-gray-900 text-white text-sm rounded-md\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-4\nspace-y-3\nw-full px-3 py-2 border rounded-md shadow-sm text-sm\nw-full px-3 py-2 border rounded-md shadow-sm text-sm\nw-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white\nw-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-3\nbg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nfont-semibold mb-3\nflex flex-wrap gap-2\npx-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium\npx-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium\npx-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium\nbg-white rounded-lg shadow-sm p-6 mb-4 border\nw-full h-32 bg-gradient-to-br from-amber-400 to-rose-500 rounded-lg shadow-inner mb-4\nfont-semibold\ntext-sm text-gray-600 mt-1\ngrid grid-cols-3 gap-2 mb-4\nbg-white rounded-lg shadow-sm p-4 border\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2\nbg-white rounded-lg shadow-sm p-4 border-2 border-blue-500\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2\nbg-white rounded-lg shadow-sm p-4 border\nfont-semibold mb-1 text-sm\ntext-lg font-bold mb-2"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Italic-Serif-Display — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1>/<h2>/<blockquote>\n text so the test can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: Fraunces 88px italic — the Lumina hero pattern */\n .hero-fraunces {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 88px;\n font-weight: 400;\n line-height: 1.05;\n margin: 0;\n }\n\n /* 2: Recoleta 64px italic */\n .hero-recoleta {\n font-family: 'Recoleta', serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 400;\n margin: 0;\n }\n\n /* 3: Playfair Display 72px italic */\n .hero-playfair {\n font-family: 'Playfair Display', serif;\n font-style: italic;\n font-size: 72px;\n font-weight: 500;\n margin: 0;\n }\n\n /* 4: Unknown serif primary, but generic `serif` fallback in stack */\n .hero-unknown-serif {\n font-family: 'CustomSerifFace', serif;\n font-style: italic;\n font-size: 56px;\n font-weight: 400;\n margin: 0;\n }\n\n /* ── PASS cases ── */\n\n /* a: italic, but sans-serif primary (Karla) — italic display in a sans is rare and usually intentional */\n .pass-sans-italic {\n font-family: 'Karla', sans-serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 600;\n margin: 0;\n }\n\n /* b: serif at 88px, but font-style: normal */\n .pass-roman-serif {\n font-family: 'Fraunces', Georgia, serif;\n font-style: normal;\n font-size: 88px;\n font-weight: 600;\n margin: 0;\n }\n\n /* c: italic serif blockquote at 24px (genuine pull quote, not a hero) */\n .pass-pull-quote {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 24px;\n font-weight: 400;\n margin: 0;\n color: #334155;\n }\n\n /* d: roman h1 with an italic <em> inside — v1 explicitly does NOT flag this */\n .pass-roman-h1-em-italic {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 72px;\n font-weight: 600;\n margin: 0;\n }\n .pass-roman-h1-em-italic em {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-weight: 400;\n }\n\n /* e: italic serif h2 at 32px — under the 48px hero threshold */\n .pass-italic-serif-32 {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 32px;\n font-weight: 400;\n margin: 0;\n }\n\n /* f: baseline-clean h1 — sans-serif roman */\n .pass-h1-sans-roman {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 64px;\n font-weight: 700;\n margin: 0;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n\n <div class=\"case\">\n <h1 class=\"hero-fraunces\">Fraunces 88px italic</h1>\n <p>The Lumina hero pattern: oversized Fraunces italic display.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-recoleta\">Recoleta 64px italic</h1>\n <p>Recoleta italic at 64px, generic serif fallback.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-playfair\">Playfair 72px italic</h1>\n <p>Playfair Display italic at hero scale.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"hero-unknown-serif\">Unknown Serif Generic Fallback</h1>\n <p>Primary font unknown to the detector, but the stack ends in generic serif.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n\n <div class=\"case\">\n <h1 class=\"pass-sans-italic\">Sans Italic Display</h1>\n <p>Italic at hero scale, but in a sans-serif face.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-roman-serif\">Roman Serif Display</h1>\n <p>Fraunces at 88px, but font-style is normal (not italic).</p>\n </div>\n\n <div class=\"case\">\n <blockquote class=\"pass-pull-quote\">Italic Serif Pull Quote</blockquote>\n <p>Italic serif at 24px is a legitimate pull-quote pattern, not a hero.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-roman-h1-em-italic\">Inline Em Inside <em>Roman</em></h1>\n <p>Roman h1 with an inline italic em — v1 deliberately does not flag this.</p>\n </div>\n\n <div class=\"case\">\n <h2 class=\"pass-italic-serif-32\">Italic Serif at 32px</h2>\n <p>Italic serif h2 below the 48px hero threshold.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-h1-sans-roman\">h1 Sans-Serif Roman</h1>\n <p>Baseline-clean control: sans-serif, roman, hero scale.</p>\n </div>\n </div>\n\n </div>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1>/<h2>/<blockquote>\n text so the test can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n\n /* ── FLAG cases ── */\n\n /* 1: Fraunces 88px italic — the Lumina hero pattern */\n .hero-fraunces {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 88px;\n font-weight: 400;\n line-height: 1.05;\n margin: 0;\n }\n\n /* 2: Recoleta 64px italic */\n .hero-recoleta {\n font-family: 'Recoleta', serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 400;\n margin: 0;\n }\n\n /* 3: Playfair Display 72px italic */\n .hero-playfair {\n font-family: 'Playfair Display', serif;\n font-style: italic;\n font-size: 72px;\n font-weight: 500;\n margin: 0;\n }\n\n /* 4: Unknown serif primary, but generic `serif` fallback in stack */\n .hero-unknown-serif {\n font-family: 'CustomSerifFace', serif;\n font-style: italic;\n font-size: 56px;\n font-weight: 400;\n margin: 0;\n }\n\n /* ── PASS cases ── */\n\n /* a: italic, but sans-serif primary (Karla) — italic display in a sans is rare and usually intentional */\n .pass-sans-italic {\n font-family: 'Karla', sans-serif;\n font-style: italic;\n font-size: 64px;\n font-weight: 600;\n margin: 0;\n }\n\n /* b: serif at 88px, but font-style: normal */\n .pass-roman-serif {\n font-family: 'Fraunces', Georgia, serif;\n font-style: normal;\n font-size: 88px;\n font-weight: 600;\n margin: 0;\n }\n\n /* c: italic serif blockquote at 24px (genuine pull quote, not a hero) */\n .pass-pull-quote {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 24px;\n font-weight: 400;\n margin: 0;\n color: #334155;\n }\n\n /* d: roman h1 with an italic <em> inside — v1 explicitly does NOT flag this */\n .pass-roman-h1-em-italic {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 72px;\n font-weight: 600;\n margin: 0;\n }\n .pass-roman-h1-em-italic em {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-weight: 400;\n }\n\n /* e: italic serif h2 at 32px — under the 48px hero threshold */\n .pass-italic-serif-32 {\n font-family: 'Fraunces', Georgia, serif;\n font-style: italic;\n font-size: 32px;\n font-weight: 400;\n margin: 0;\n }\n\n /* f: baseline-clean h1 — sans-serif roman */\n .pass-h1-sans-roman {\n font-family: 'Inter', sans-serif;\n font-style: normal;\n font-size: 64px;\n font-weight: 700;\n margin: 0;\n }\n ","classText":"grid\ncol\ncol-label\ncase\nhero-fraunces\ncase\nhero-recoleta\ncase\nhero-playfair\ncase\nhero-unknown-serif\ncol\ncol-label\ncase\npass-sans-italic\ncase\npass-roman-serif\ncase\npass-pull-quote\ncase\npass-roman-h1-em-italic\ncase\npass-italic-serif-32\ncase\npass-h1-sans-roman"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Hero-Eyebrow-Chip — Should Flag vs Should Pass</title>\n <style>\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n .case h1 { margin: 0; font-family: 'Inter', sans-serif; line-height: 1.05; }\n\n /* Hero size used by most cases */\n .hero { font-size: 88px; font-weight: 600; }\n\n /* ── FLAG cases ── */\n\n .eyebrow-classic {\n text-transform: uppercase;\n letter-spacing: 0.15em;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-span {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n font-size: 11px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-pill {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n background: #f1f5f9;\n color: #334155;\n border-radius: 999px;\n padding: 4px 12px;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-already-uppercase {\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #b45309;\n font-weight: 700;\n margin: 0 0 16px;\n }\n\n /* ── PASS cases ── */\n\n .pass-eyebrow-no-tracking {\n text-transform: uppercase;\n letter-spacing: normal;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .pass-body-heading {\n font-size: 24px;\n font-weight: 600;\n font-family: 'Inter', sans-serif;\n margin: 0;\n }\n\n .pass-card-caption {\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 600;\n margin: 0 0 8px;\n }\n\n .pass-bare-hero {\n font-size: 64px;\n }\n\n .pass-h2-above-h1 {\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-weight: 700;\n color: #475569;\n margin: 0 0 16px;\n font-family: 'Inter', sans-serif;\n }\n\n .pass-long-uppercase {\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2 class=\"col-label\">Should flag</h2>\n\n <div class=\"case\">\n <div class=\"eyebrow-classic\">AI-NATIVE WORKFLOWS</div>\n <h1 class=\"hero\">Eyebrow Above Hero</h1>\n <p>Classic uppercase tracked div eyebrow above an 88px hero.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-span\">NEW IN 2026</span>\n <h1 class=\"hero\">Span Eyebrow Above Hero</h1>\n <p>Span variant with the same uppercase + tracking + small-size shape.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-pill\">FEATURED</span>\n <h1 class=\"hero\">Pill Chip Above Hero</h1>\n <p>Pill-shaped chip with background, border-radius, padding.</p>\n </div>\n\n <div class=\"case\">\n <span class=\"eyebrow-already-uppercase\">NEW</span>\n <h1 class=\"hero\">Already Uppercase Text</h1>\n <p>Text typed uppercase, no text-transform, but matching tracking and size.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"pass-long-uppercase\">A VERY LONG UPPERCASE TABLE OF CONTENTS HEADER</div>\n <h1 class=\"hero\">Long Uppercase Sentence Above Hero</h1>\n <p>46-char uppercase tracked label — under the 60-char eyebrow ceiling, so it still reads as an oversized eyebrow.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2 class=\"col-label\">Should pass</h2>\n\n <div class=\"case\">\n <div class=\"pass-eyebrow-no-tracking\">UPPERCASE LABEL</div>\n <h1 class=\"hero\">Eyebrow With Normal Tracking</h1>\n <p>Uppercase label above a hero but with letter-spacing: normal.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"pass-card-caption\">CARD CAPTION</div>\n <p>Plain body text below the caption.</p>\n <h1 class=\"hero\" style=\"margin-top:32px\">Uppercase Caption Far From Hero</h1>\n <p>Hero exists, but its preceding sibling is a paragraph, not the eyebrow.</p>\n </div>\n\n <div class=\"case\">\n <h1 class=\"pass-bare-hero\">Hero With No Eyebrow</h1>\n <p>Bare h1 at 64px, nothing above it.</p>\n </div>\n\n <div class=\"case\">\n <h2 class=\"pass-h2-above-h1\">SECTION HEADING</h2>\n <h1 class=\"hero\">Heading Above Heading</h1>\n <p>An h2 styled like an eyebrow above an h1 — heading-tag exclusion must skip this.</p>\n </div>\n\n <div class=\"case\">\n <div class=\"eyebrow-classic\">SECTION KICKER</div>\n <h1 class=\"pass-body-heading\">Body-Sized Heading Below Eyebrow</h1>\n <p>A compact product/page heading is not an oversized marketing hero.</p>\n </div>\n\n <div class=\"case\" role=\"tabpanel\" aria-label=\"Saved station\">\n <div class=\"eyebrow-classic\">NEXT FROM</div>\n <h1 class=\"hero\">Application Panel Heading</h1>\n <p>Context labels inside tab panels describe application state rather than decorating a marketing hero.</p>\n </div>\n\n </div>\n\n </div>\n</body>\n</html>\n",{"styleText":"\n /* ─── Layout convention: two-column fixture ─────────────────────────\n Left column = patterns the rule SHOULD flag.\n Right column = legitimate patterns the rule should NOT flag.\n Each test case is annotated with a unique <h1> text so the test\n can match snippets back to expectations.\n ──────────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2.col-label { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .case { margin-bottom: 32px; padding: 24px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }\n .case p { font-size: 14px; margin: 12px 0 0; color: #64748b; }\n .case h1 { margin: 0; font-family: 'Inter', sans-serif; line-height: 1.05; }\n\n /* Hero size used by most cases */\n .hero { font-size: 88px; font-weight: 600; }\n\n /* ── FLAG cases ── */\n\n .eyebrow-classic {\n text-transform: uppercase;\n letter-spacing: 0.15em;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-span {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n font-size: 11px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-pill {\n display: inline-block;\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n background: #f1f5f9;\n color: #334155;\n border-radius: 999px;\n padding: 4px 12px;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .eyebrow-already-uppercase {\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #b45309;\n font-weight: 700;\n margin: 0 0 16px;\n }\n\n /* ── PASS cases ── */\n\n .pass-eyebrow-no-tracking {\n text-transform: uppercase;\n letter-spacing: normal;\n font-size: 12px;\n color: #64748b;\n font-weight: 600;\n margin: 0 0 16px;\n }\n\n .pass-body-heading {\n font-size: 24px;\n font-weight: 600;\n font-family: 'Inter', sans-serif;\n margin: 0;\n }\n\n .pass-card-caption {\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 600;\n margin: 0 0 8px;\n }\n\n .pass-bare-hero {\n font-size: 64px;\n }\n\n .pass-h2-above-h1 {\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n font-weight: 700;\n color: #475569;\n margin: 0 0 16px;\n font-family: 'Inter', sans-serif;\n }\n\n .pass-long-uppercase {\n text-transform: uppercase;\n letter-spacing: 0.14em;\n font-size: 12px;\n color: #475569;\n font-weight: 600;\n margin: 0 0 16px;\n }\n \nstyle=\"margin-top:32px\"","classText":"grid\ncol\ncol-label\ncase\neyebrow-classic\nhero\ncase\neyebrow-span\nhero\ncase\neyebrow-pill\nhero\ncase\neyebrow-already-uppercase\nhero\ncase\npass-long-uppercase\nhero\ncol\ncol-label\ncase\npass-eyebrow-no-tracking\nhero\ncase\npass-card-caption\nhero\ncase\npass-bare-hero\ncase\npass-h2-above-h1\nhero\ncase\neyebrow-classic\npass-body-heading\ncase\neyebrow-classic\nhero"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Kicker Above Heading Fixture</title>\n <style>\n body {\n margin: 0;\n font-family: Arial, sans-serif;\n background: #f6f2ec;\n color: #171411;\n }\n main {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n padding: 32px;\n }\n .col {\n min-width: 0;\n border: 1px solid #c9bfb2;\n padding: 24px;\n }\n .case {\n min-height: 120px;\n margin: 0 0 24px;\n padding: 16px;\n background: #fffaf3;\n }\n .kicker,\n .pass-kicker,\n .pass-kicker-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n color: #5b5046;\n }\n .kicker,\n .pass-kicker-caps {\n text-transform: uppercase;\n }\n /* The wild's most common kicker recipe: 0.08em tracking at a sub-13px\n size computes to under 1px, which the old absolute floor excluded. */\n .tight-tracked-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .smallcaps-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.12em;\n font-variant: small-caps;\n color: #5b5046;\n }\n .untracked-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: normal;\n text-transform: uppercase;\n color: #5b5046;\n }\n h1,\n h2 {\n margin: 0 0 12px;\n font-size: 32px;\n line-height: 38px;\n font-weight: 700;\n }\n h3 {\n margin: 0 0 12px;\n font-size: 24px;\n line-height: 30px;\n font-weight: 700;\n }\n h4 {\n margin: 0 0 12px;\n font-size: 20px;\n line-height: 26px;\n font-weight: 700;\n }\n .role-heading {\n margin: 0 0 12px;\n font-size: 28px;\n line-height: 34px;\n font-weight: 700;\n }\n .hero-h1 {\n margin: 0 0 12px;\n font-size: 64px;\n line-height: 70px;\n font-weight: 700;\n }\n .hero-eyebrow {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #5b5046;\n }\n p,\n label,\n figcaption,\n a,\n li {\n font-size: 16px;\n line-height: 24px;\n }\n nav,\n form,\n figure,\n ol {\n margin: 0 0 24px;\n padding: 16px;\n border: 1px solid #d8cec0;\n min-height: 80px;\n }\n .card-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 0 0 24px;\n }\n .suite-card {\n display: block;\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .taxonomy-label {\n display: block;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .suite-card h3 {\n font-size: 24px;\n line-height: 30px;\n }\n .stat {\n min-height: 100px;\n padding: 16px;\n background: #fffaf3;\n }\n .stat h3 {\n font-size: 40px;\n line-height: 46px;\n }\n .hidden-mockup {\n display: grid;\n gap: 12px;\n margin: 0 0 24px;\n }\n .mockup-hero {\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .mockup-hero h2 {\n font-size: 24px;\n line-height: 30px;\n }\n </style>\n</head>\n<body>\n <main>\n <section class=\"col\" aria-label=\"Should flag\">\n <h1>Should flag</h1>\n\n <section class=\"case\">\n <span class=\"kicker\">Invitation protocol</span>\n <h2>\"A Single Kicker Still Flags\"</h2>\n <p>One tracked uppercase label above one heading is enough. No repetition required.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"tight-tracked-kicker\">Output model</span>\n <h2>\"Standard Tracking Kicker\"</h2>\n <p>Uppercase at 12px with 0.08em tracking is the most common authored kicker and must flag.</p>\n </section>\n\n <section class=\"case\">\n <p class=\"kicker\">Preview sequence</p>\n <h3>\"Kicker Above An H3\"</h3>\n <p>Subsection headings get no eyebrow either.</p>\n </section>\n\n <section class=\"case\">\n <div class=\"kicker\">Access window</div>\n <h4>\"Kicker Above An H4\"</h4>\n <p>The ban runs down to h4.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"kicker\">Platform notes</span>\n <h1 class=\"sub-hero\">\"Sub Hero Heading\"</h1>\n <p>A 32px h1 is below hero scale, so this pair belongs to the general ban.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"smallcaps-kicker\">materials briefing</span>\n <h2>\"Small Caps Kicker\"</h2>\n <p>Small-caps styling is the same label in a different costume.</p>\n </section>\n\n <section class=\"case\">\n <div class=\"kicker\">Field studies</div>\n <div role=\"heading\" aria-level=\"2\" class=\"role-heading\">\"Heading Role Kicker\"</div>\n <p>Elements carrying a heading role are headings for this rule.</p>\n </section>\n </section>\n\n <section class=\"col\" aria-label=\"Should pass\">\n <h1>Should pass</h1>\n\n <nav class=\"case\" aria-label=\"Breadcrumb\">\n <span class=\"pass-kicker\">Home / Journal</span>\n <h2>\"Breadcrumb Before Heading\"</h2>\n <a href=\"/\">Home</a>\n </nav>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Home / Journal / Archive</span>\n <h2>\"Breadcrumb Trail Outside Nav\"</h2>\n <p>A path with separators is wayfinding, not a kicker, even without a nav wrapper.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">News · Jan 12, 2026</span>\n <h2>\"Dateline Above Headline\"</h2>\n <p>Editorial category-and-date meta lines are content metadata.</p>\n </section>\n\n <article class=\"case\">\n <span class=\"pass-kicker-caps\">Field report</span>\n <h3>\"Editorial Card Meta\"</h3>\n <p>Category labels inside an article card are structured metadata for scanning.</p>\n </article>\n\n <form class=\"case\">\n <label class=\"pass-kicker\" for=\"guest\">Guest name</label>\n <input id=\"guest\" name=\"guest\" style=\"width: 220px; height: 40px;\">\n <h2>\"Form Heading Is Separate\"</h2>\n </form>\n\n <figure class=\"case\">\n <figcaption class=\"pass-kicker-caps\">Plate study</figcaption>\n <h3>\"Figure Caption Label\"</h3>\n </figure>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Section 4.2</span>\n <h2>\"Limitation Of Liability\"</h2>\n <p>Legal and document numbering identifies the clause; it is not decoration.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">§ 12.3</span>\n <h2>\"Indemnification Clause\"</h2>\n <p>Statutory references keep their marker.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"pass-kicker-caps\">Chapter Four</span>\n <h2>\"Chapter Numbering Passes\"</h2>\n <p>Spelled-out chapter numbering is document structure, like legal numbering.</p>\n </section>\n\n <section class=\"case\" role=\"tabpanel\" aria-label=\"Saved station\">\n <span class=\"pass-kicker-caps\">Next from</span>\n <h2>\"Application Panel Context\"</h2>\n <p>Context labels inside application panels describe state, not decoration.</p>\n </section>\n\n <section class=\"case\">\n <nav aria-label=\"Primary\"><a href=\"/\">Home</a> <a href=\"/docs\">Docs</a></nav>\n <h1>\"Page Title After Nav\"</h1>\n <p>A navigation landmark preceding the page title is chrome, not a kicker.</p>\n </section>\n\n <div class=\"stat\">\n <h3>\"48%\"</h3>\n <span class=\"pass-kicker-caps\">Growth yoy</span>\n <p>Stat callouts put the small label below the number. That direction is legal.</p>\n </div>\n\n <section class=\"case\">\n <span class=\"pass-kicker\">A quiet lead-in sentence</span>\n <h2>\"Sentence Case Lead In\"</h2>\n <p>Not uppercase, not small-caps: an ordinary standfirst.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"untracked-caps\">New arrivals</span>\n <h2>\"Untracked Caps Label\"</h2>\n <p>Uppercase without tracking lacks the kicker styling signature.</p>\n </section>\n\n <section class=\"case\" data-impeccable-allow-kickers>\n <span class=\"pass-kicker-caps\">Archive code</span>\n <h2>\"Intentional Brand Label\"</h2>\n <p>Deliberate brand systems can opt out with an explicit marker.</p>\n </section>\n\n <section class=\"case\">\n <span class=\"hero-eyebrow\">Platform overview</span>\n <h1 class=\"hero-h1\">\"Hero Owned By Hero Rule\"</h1>\n <p>Hero-scale h1 eyebrows stay with hero-eyebrow-chip; this rule stands down.</p>\n </section>\n\n <div class=\"card-grid\" aria-label=\"Room cards\">\n <article class=\"suite-card\">\n <span class=\"taxonomy-label\">Suite</span>\n <h3>\"Garden Suite\"</h3>\n <p>Category labels inside sibling cards are metadata for comparison.</p>\n </article>\n <article class=\"suite-card\">\n <span class=\"taxonomy-label\">Suite</span>\n <h3>\"Sea Loft\"</h3>\n <p>The label is useful because the user is scanning sibling cards.</p>\n </article>\n </div>\n\n <ol class=\"case\">\n <li>\n <span class=\"pass-kicker-caps\">Step 01</span>\n <h3>\"Step Indicator\"</h3>\n <p>Step indicators in ordered flows are sequence markers.</p>\n </li>\n </ol>\n\n <div class=\"hidden-mockup\" aria-hidden=\"true\">\n <div class=\"mockup-hero\">\n <span class=\"taxonomy-label\">Amalfi Coast</span>\n <h2>\"Mockup Hero Variant One\"</h2>\n </div>\n </div>\n </section>\n </main>\n</body>\n</html>\n",{"styleText":"\n body {\n margin: 0;\n font-family: Arial, sans-serif;\n background: #f6f2ec;\n color: #171411;\n }\n main {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n padding: 32px;\n }\n .col {\n min-width: 0;\n border: 1px solid #c9bfb2;\n padding: 24px;\n }\n .case {\n min-height: 120px;\n margin: 0 0 24px;\n padding: 16px;\n background: #fffaf3;\n }\n .kicker,\n .pass-kicker,\n .pass-kicker-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n color: #5b5046;\n }\n .kicker,\n .pass-kicker-caps {\n text-transform: uppercase;\n }\n /* The wild's most common kicker recipe: 0.08em tracking at a sub-13px\n size computes to under 1px, which the old absolute floor excluded. */\n .tight-tracked-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .smallcaps-kicker {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.12em;\n font-variant: small-caps;\n color: #5b5046;\n }\n .untracked-caps {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: normal;\n text-transform: uppercase;\n color: #5b5046;\n }\n h1,\n h2 {\n margin: 0 0 12px;\n font-size: 32px;\n line-height: 38px;\n font-weight: 700;\n }\n h3 {\n margin: 0 0 12px;\n font-size: 24px;\n line-height: 30px;\n font-weight: 700;\n }\n h4 {\n margin: 0 0 12px;\n font-size: 20px;\n line-height: 26px;\n font-weight: 700;\n }\n .role-heading {\n margin: 0 0 12px;\n font-size: 28px;\n line-height: 34px;\n font-weight: 700;\n }\n .hero-h1 {\n margin: 0 0 12px;\n font-size: 64px;\n line-height: 70px;\n font-weight: 700;\n }\n .hero-eyebrow {\n display: block;\n width: 240px;\n min-height: 16px;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #5b5046;\n }\n p,\n label,\n figcaption,\n a,\n li {\n font-size: 16px;\n line-height: 24px;\n }\n nav,\n form,\n figure,\n ol {\n margin: 0 0 24px;\n padding: 16px;\n border: 1px solid #d8cec0;\n min-height: 80px;\n }\n .card-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin: 0 0 24px;\n }\n .suite-card {\n display: block;\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .taxonomy-label {\n display: block;\n margin: 0 0 8px;\n font-size: 12px;\n line-height: 16px;\n letter-spacing: 0.11em;\n text-transform: uppercase;\n color: #5b5046;\n }\n .suite-card h3 {\n font-size: 24px;\n line-height: 30px;\n }\n .stat {\n min-height: 100px;\n padding: 16px;\n background: #fffaf3;\n }\n .stat h3 {\n font-size: 40px;\n line-height: 46px;\n }\n .hidden-mockup {\n display: grid;\n gap: 12px;\n margin: 0 0 24px;\n }\n .mockup-hero {\n min-height: 120px;\n padding: 16px;\n border: 1px solid #d8cec0;\n background: #fff;\n }\n .mockup-hero h2 {\n font-size: 24px;\n line-height: 30px;\n }\n \nstyle=\"width: 220px; height: 40px;\"","classText":"col\ncase\nkicker\ncase\ntight-tracked-kicker\ncase\nkicker\ncase\nkicker\ncase\nkicker\nsub-hero\ncase\nsmallcaps-kicker\ncase\nkicker\nrole-heading\ncol\ncase\npass-kicker\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\npass-kicker-caps\ncase\nstat\npass-kicker-caps\ncase\npass-kicker\ncase\nuntracked-caps\ncase\npass-kicker-caps\ncase\nhero-eyebrow\nhero-h1\ncard-grid\nsuite-card\ntaxonomy-label\nsuite-card\ntaxonomy-label\ncase\npass-kicker-caps\nhidden-mockup\nmockup-hero\ntaxonomy-label"}],"result":[]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Motion Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n /* ─── Two-column fixture convention ─────────────────────────────\n Left column = should flag. Right column = should pass.\n Tests assert (a) every flag-column rule fires and (b) the total\n finding count matches the number of flag cases (no false\n positives leaking from the pass column).\n ──────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .demo { padding: 14px 16px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 10px; }\n .demo h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .demo p { font-size: 12px; color: #6b7280; margin: 4px 0 0; }\n\n /* ── FLAG: bounce / elastic easing ── */\n @keyframes bounce-keyframe {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n .bounce-animation { animation: bounce-keyframe 1s infinite; }\n .elastic-transition { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }\n\n /* ── FLAG: layout property transitions ── */\n .width-transition { transition: width 0.3s ease; }\n .height-transition { transition: height 0.4s ease-out; }\n .padding-transition { transition: padding 0.2s linear; }\n .margin-transition { transition: margin 0.3s ease-in; }\n .max-height-transition { transition: max-height 0.5s ease; }\n .multi-layout-transition { transition: width 0.3s ease, height 0.3s ease; }\n .mixed-transition { transition: width 0.3s ease, opacity 0.3s ease; }\n .transition-property-width { transition-property: width; transition-duration: 0.3s; }\n\n /* ── PASS: good easing ── */\n @keyframes fade-in-keyframe {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .fade-in-good { animation: fade-in-keyframe 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n .ease-out-quart { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }\n .ease-out-expo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }\n\n /* ── PASS: safe transitions ── */\n .transform-transition { transition: transform 0.3s ease-out; }\n .opacity-transition { transition: opacity 0.2s ease; }\n .color-transition { transition: color 0.15s ease, background-color 0.15s ease; }\n .shadow-transition { transition: box-shadow 0.2s ease; }\n .all-transition { transition: all 0.3s ease; }\n .multi-safe-transition { transition: transform 0.3s ease, opacity 0.3s ease, box-shadow 0.2s ease; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Bounce / elastic easing</h3>\n <div class=\"demo bounce-animation\">\n <h4>CSS bounce animation</h4>\n <p>animation: bounce 1s infinite</p>\n </div>\n <div class=\"demo elastic-transition\">\n <h4>Elastic cubic-bezier</h4>\n <p>cubic-bezier(0.68, -0.55, 0.265, 1.55)</p>\n </div>\n\n <h3>Layout property transitions</h3>\n <div class=\"demo width-transition\">\n <h4>transition: width</h4>\n <p>Animating width causes layout thrash.</p>\n </div>\n <div class=\"demo height-transition\">\n <h4>transition: height</h4>\n <p>Animating height causes layout thrash.</p>\n </div>\n <div class=\"demo padding-transition\">\n <h4>transition: padding</h4>\n <p>Animating padding causes layout thrash.</p>\n </div>\n <div class=\"demo margin-transition\">\n <h4>transition: margin</h4>\n <p>Animating margin causes layout thrash.</p>\n </div>\n <div class=\"demo max-height-transition\">\n <h4>transition: max-height</h4>\n <p>Use grid-template-rows instead.</p>\n </div>\n <div class=\"demo multi-layout-transition\">\n <h4>transition: width, height</h4>\n <p>Multiple layout properties.</p>\n </div>\n <div class=\"demo mixed-transition\">\n <h4>transition: width, opacity</h4>\n <p>Layout property mixed with OK property.</p>\n </div>\n <div class=\"demo transition-property-width\">\n <h4>transition-property: width</h4>\n <p>Longhand form.</p>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Good easing</h3>\n <div class=\"demo fade-in-good\">\n <h4>Smooth fade in</h4>\n <p>animation: fadeIn with exponential ease-out</p>\n </div>\n <div class=\"demo ease-out-quart\">\n <h4>Ease-out quart</h4>\n <p>cubic-bezier(0.25, 1, 0.5, 1) — smooth deceleration</p>\n </div>\n <div class=\"demo ease-out-expo\">\n <h4>Ease-out expo</h4>\n <p>cubic-bezier(0.16, 1, 0.3, 1) — natural feel</p>\n </div>\n\n <h3>Safe transitions (transform / opacity / color only)</h3>\n <div class=\"demo transform-transition\">\n <h4>transition: transform</h4>\n <p>Transform is GPU-accelerated and safe.</p>\n </div>\n <div class=\"demo opacity-transition\">\n <h4>transition: opacity</h4>\n <p>Opacity is GPU-accelerated and safe.</p>\n </div>\n <div class=\"demo color-transition\">\n <h4>transition: color, background-color</h4>\n <p>Color transitions are paint-only, no layout.</p>\n </div>\n <div class=\"demo shadow-transition\">\n <h4>transition: box-shadow</h4>\n <p>Shadow transitions are paint-only.</p>\n </div>\n <div class=\"demo all-transition\">\n <h4>transition: all</h4>\n <p>Too common to flag — usually paired with transform/opacity.</p>\n </div>\n <div class=\"demo multi-safe-transition\">\n <h4>transition: transform, opacity, box-shadow</h4>\n <p>Multiple safe properties combined.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* ─── Two-column fixture convention ─────────────────────────────\n Left column = should flag. Right column = should pass.\n Tests assert (a) every flag-column rule fires and (b) the total\n finding count matches the number of flag cases (no false\n positives leaking from the pass column).\n ──────────────────────────────────────────────────────────── */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .demo { padding: 14px 16px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 10px; }\n .demo h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .demo p { font-size: 12px; color: #6b7280; margin: 4px 0 0; }\n\n /* ── FLAG: bounce / elastic easing ── */\n @keyframes bounce-keyframe {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n .bounce-animation { animation: bounce-keyframe 1s infinite; }\n .elastic-transition { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }\n\n /* ── FLAG: layout property transitions ── */\n .width-transition { transition: width 0.3s ease; }\n .height-transition { transition: height 0.4s ease-out; }\n .padding-transition { transition: padding 0.2s linear; }\n .margin-transition { transition: margin 0.3s ease-in; }\n .max-height-transition { transition: max-height 0.5s ease; }\n .multi-layout-transition { transition: width 0.3s ease, height 0.3s ease; }\n .mixed-transition { transition: width 0.3s ease, opacity 0.3s ease; }\n .transition-property-width { transition-property: width; transition-duration: 0.3s; }\n\n /* ── PASS: good easing ── */\n @keyframes fade-in-keyframe {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .fade-in-good { animation: fade-in-keyframe 0.4s cubic-bezier(0.16, 1, 0.3, 1); }\n .ease-out-quart { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }\n .ease-out-expo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }\n\n /* ── PASS: safe transitions ── */\n .transform-transition { transition: transform 0.3s ease-out; }\n .opacity-transition { transition: opacity 0.2s ease; }\n .color-transition { transition: color 0.15s ease, background-color 0.15s ease; }\n .shadow-transition { transition: box-shadow 0.2s ease; }\n .all-transition { transition: all 0.3s ease; }\n .multi-safe-transition { transition: transform 0.3s ease, opacity 0.3s ease, box-shadow 0.2s ease; }\n ","classText":"grid\ncol\ndemo bounce-animation\ndemo elastic-transition\ndemo width-transition\ndemo height-transition\ndemo padding-transition\ndemo margin-transition\ndemo max-height-transition\ndemo multi-layout-transition\ndemo mixed-transition\ndemo transition-property-width\ncol\ndemo fade-in-good\ndemo ease-out-quart\ndemo ease-out-expo\ndemo transform-transition\ndemo opacity-transition\ndemo color-transition\ndemo shadow-transition\ndemo all-transition\ndemo multi-safe-transition"}],"result":[{"id":"bounce-easing","snippet":"animation: bounce-keyframe","selector":".bounce-animation"},{"id":"bounce-easing","snippet":"cubic-bezier(0.68, -0.55, 0.265, 1.55)","selector":".elastic-transition"},{"id":"layout-transition","snippet":"transition: width"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Dark Glow Anti-Patterns — Should Flag vs Should Pass</title>\n <style>\n /* Two-column fixture: left = should flag, right = should pass.\n The dark-glow rule fires when a colored box-shadow appears on a\n child whose ancestor has a dark background. Each column wraps\n its dark-context cases in its own .dark-context section so the\n parent-bg lookup behaves predictably regardless of layout. */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .dark-context { background: #111827; padding: 16px; border-radius: 12px; }\n .dark-context + .dark-context { margin-top: 12px; }\n /* Card variants: explicit dark vs light pairings, both WCAG AA compliant */\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .card h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .card p { font-size: 12px; margin: 4px 0 0; }\n .card-dark { background: #1f2937; }\n .card-dark h4 { color: #f9fafb; }\n .card-dark p { color: #cbd5e1; }\n .card-light { background: white; }\n .card-light h4 { color: #0f172a; }\n .card-light p { color: #475569; }\n\n /* ── FLAG: dark background + colored glow ── */\n .glow-blue { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }\n .glow-purple { box-shadow: 0 0 25px rgba(139, 92, 246, 0.35); }\n .glow-cyan { box-shadow: 0 0 15px rgba(6, 182, 212, 0.5); }\n .glow-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 0 30px rgba(168, 85, 247, 0.3); }\n\n /* ── FLAG: modern color formats, var() refs, text-shadow ── */\n :root { --fixture-glow: oklch(0.78 0.14 155); }\n .glow-oklch { box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5); }\n .glow-hex { box-shadow: 0 0 16px #3b82f6; }\n .glow-hsl { box-shadow: 0 0 22px hsl(280, 80%, 60%); }\n .glow-var { box-shadow: 0 0 10px var(--fixture-glow); }\n .glow-text h4 { text-shadow: 0 0 12px #22d3ee; }\n /* Zero-offset chromatic halo is slop on light backgrounds too */\n .glow-light-oklch { box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45); }\n /* Unreadable surface: url() image ancestor. The zero-offset halo tell\n holds on ANY background, so an unknown surface must not suppress it.\n Both element loops must agree here (the browser adapter once returned\n [] for the whole element on an unresolved surface). */\n .photo-context { background-image: url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-halo { box-shadow: 0 0 26px rgba(217, 70, 239, 0.5); }\n /* Opaque gradient atop a url() layer: every stop is opaque, so the\n gradient provably covers the image — the surface IS the dark\n gradient, and the dark-background glow tell may score against it\n (browser hunt; the static loop has no gradient hunt). */\n .photo-opaque-grad { background: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-opaque { box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5); }\n\n /* ── PASS: same dark/light backgrounds, but neutral or no glow ── */\n .light-colored-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.15); }\n .dark-normal-shadow { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); }\n .dark-focus-ring { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); }\n .dark-subtle-shadow { box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2); }\n .dark-elevated-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); }\n .dark-elevated-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); }\n .dark-elevated-lg { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); }\n .dark-elevated-inset { box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); }\n .dark-elevated-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); }\n .elevated-sm-light { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }\n .elevated-md-light { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }\n .elevated-warm-light { box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.15); }\n /* Medium-gray card: lighter than dark mode, used to demonstrate \"not dark enough\" for glow detection. White text for AA contrast. */\n .card-medium { background: #6b7280; }\n .card-medium h4 { color: #ffffff; }\n .card-medium p { color: #ffffff; }\n .medium-gray-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3); }\n /* Offset chromatic drop shadow on a light background: a real drop\n shadow, not a halo — must NOT flag under the zero-offset rule.\n (Element path; the page-level text scan caps at one finding.) */\n .light-offset-colored { box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25); }\n /* Achromatic zero-offset shadow: soft ambient elevation, stays legal */\n .light-soft-neutral { box-shadow: 0 0 24px rgba(0, 0, 0, 0.15); }\n /* Offset chromatic shadow under the same unreadable url() surface:\n the dark-background glow tell needs a surface we can read — abstain. */\n .photo-offset-colored { box-shadow: 0 6px 20px rgba(16, 185, 129, 0.35); }\n /* Translucent gradient atop a url() layer: the image shows through the\n 20% wash, so the real surface is a blend with unknowable pixels (a\n white photo composites to ~#cccccc, not black). Averaging the wash\n stops as if they were the surface scored this \"dark\" — abstain. */\n .photo-translucent-grad { background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .photo-translucent-offset { box-shadow: 0 6px 22px rgba(244, 63, 94, 0.45); }\n /* Offset neutral text-shadow on dark card: legibility aid, not a glow */\n .dark-text-offset h4 { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag (dark page + colored glows)\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>CSS colored glows on dark background</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark glow-blue\">\n <h4>Blue glow</h4>\n <p>box-shadow: 0 0 20px rgba(59, 130, 246, 0.4)</p>\n </div>\n <div class=\"card card-dark glow-purple\">\n <h4>Purple glow</h4>\n <p>box-shadow: 0 0 25px rgba(139, 92, 246, 0.35)</p>\n </div>\n <div class=\"card card-dark glow-cyan\">\n <h4>Cyan glow</h4>\n <p>box-shadow: 0 0 15px rgba(6, 182, 212, 0.5)</p>\n </div>\n <div class=\"card card-dark glow-multi\">\n <h4>Multi-shadow with colored glow</h4>\n <p>Normal shadow + purple glow combined.</p>\n </div>\n </div>\n\n <h3>Inline-style glow (regex path)</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark\" style=\"box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);\">\n <h4>Inline pink glow</h4>\n <p>Inline style for regex detection path.</p>\n </div>\n </div>\n\n <h3>Modern color formats, var() refs, text-shadow</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark glow-oklch\">\n <h4>OKLCH glow</h4>\n <p>box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5)</p>\n </div>\n <div class=\"card card-dark glow-hex\">\n <h4>Hex glow</h4>\n <p>box-shadow: 0 0 16px #3b82f6</p>\n </div>\n <div class=\"card card-dark glow-hsl\">\n <h4>HSL glow</h4>\n <p>box-shadow: 0 0 22px hsl(280, 80%, 60%)</p>\n </div>\n <div class=\"card card-dark glow-var\">\n <h4>Glow via var()</h4>\n <p>box-shadow: 0 0 10px var(--fixture-glow)</p>\n </div>\n <div class=\"card card-dark glow-text\">\n <h4>Glowing text</h4>\n <p>text-shadow: 0 0 12px #22d3ee on the heading.</p>\n </div>\n </div>\n\n <h3>Zero-offset halo on LIGHT background</h3>\n <div class=\"card card-light glow-light-oklch\">\n <h4>Chromatic halo on light page</h4>\n <p>box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45)</p>\n </div>\n\n <h3>Zero-offset halo on unreadable (image) surface</h3>\n <div class=\"photo-context\">\n <div class=\"card card-light glow-photo-halo\">\n <h4>Halo over photo background</h4>\n <p>box-shadow: 0 0 26px rgba(217, 70, 239, 0.5)</p>\n </div>\n </div>\n\n <h3>Colored glow under an opaque gradient over an image (browser)</h3>\n <div class=\"photo-opaque-grad\">\n <div class=\"card card-dark glow-photo-opaque\">\n <h4>Opaque dark gradient covers the photo</h4>\n <p>box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5)</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>Light page + colored shadow</h3>\n <div class=\"card card-light light-colored-shadow\">\n <h4>Colored shadow on light background</h4>\n <p>Not dark mode, colored shadow is fine.</p>\n </div>\n\n <h3>Dark cards with neutral shadows</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark dark-normal-shadow\">\n <h4>Normal gray shadow on dark card</h4>\n <p>Gray/black shadow is not a glow.</p>\n </div>\n <div class=\"card card-dark dark-focus-ring\">\n <h4>Focus ring (spread, no blur)</h4>\n <p>Functional ring, not decorative glow.</p>\n </div>\n <div class=\"card card-dark dark-subtle-shadow\">\n <h4>Tiny blur (&lt;5px)</h4>\n <p>Too subtle to be \"glowing\".</p>\n </div>\n <div class=\"card card-dark dark-elevated-sm\">\n <h4>Small dark elevation</h4>\n <p>Subtle shadow on dark card.</p>\n </div>\n <div class=\"card card-dark dark-elevated-md\">\n <h4>Medium dark elevation</h4>\n <p>Standard dark card shadow.</p>\n </div>\n <div class=\"card card-dark dark-elevated-lg\">\n <h4>Large dark elevation</h4>\n <p>Prominent shadow on dark card.</p>\n </div>\n <div class=\"card card-dark dark-elevated-inset\">\n <h4>Inset shadow</h4>\n <p>Inner shadow, not a glow.</p>\n </div>\n <div class=\"card card-dark dark-elevated-multi\">\n <h4>Multi-shadow dark card</h4>\n <p>Layered gray shadows, no color glow.</p>\n </div>\n </div>\n\n <h3>Medium-gray background (not dark enough)</h3>\n <div class=\"card card-medium medium-gray-shadow\">\n <h4>Not dark enough background</h4>\n <p>Medium gray doesn't qualify as \"dark mode\".</p>\n </div>\n\n <h3>Colored drop shadow / neutral halo on light background</h3>\n <div class=\"card card-light light-offset-colored\">\n <h4>Offset colored drop shadow</h4>\n <p>Real drop shadow with a color tint — not a halo.</p>\n </div>\n <div class=\"card card-light light-soft-neutral\">\n <h4>Soft neutral halo</h4>\n <p>Achromatic zero-offset shadow is ambient elevation.</p>\n </div>\n\n <h3>Offset chromatic shadow on unreadable (image) surface</h3>\n <div class=\"photo-context\">\n <div class=\"card card-light photo-offset-colored\">\n <h4>Offset colored shadow over photo background</h4>\n <p>Surface unknown, offset shadow — abstain, not flag.</p>\n </div>\n </div>\n\n <h3>Offset chromatic shadow under a translucent gradient over an image</h3>\n <div class=\"photo-translucent-grad\">\n <div class=\"card card-light photo-translucent-offset\">\n <h4>The wash blends with unknowable image pixels</h4>\n <p>Surface unknown — abstain, never score the wash stops as dark.</p>\n </div>\n </div>\n\n <h3>Neutral text-shadow on dark card</h3>\n <div class=\"dark-context\">\n <div class=\"card card-dark dark-text-offset\">\n <h4>Offset neutral text-shadow</h4>\n <p>Legibility aid, not a glow.</p>\n </div>\n </div>\n\n <h3>Light elevated cards</h3>\n <div class=\"card card-light elevated-sm-light\">\n <h4>Small elevation (Tailwind shadow-sm)</h4>\n <p>Standard subtle card shadow.</p>\n </div>\n <div class=\"card card-light elevated-md-light\">\n <h4>Medium elevation (Tailwind shadow-md)</h4>\n <p>Standard card shadow.</p>\n </div>\n <div class=\"card card-light elevated-warm-light\">\n <h4>Warm deep shadow</h4>\n <p>Large spread neutral shadow.</p>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Two-column fixture: left = should flag, right = should pass.\n The dark-glow rule fires when a colored box-shadow appears on a\n child whose ancestor has a dark background. Each column wraps\n its dark-context cases in its own .dark-context section so the\n parent-bg lookup behaves predictably regardless of layout. */\n body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }\n .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }\n .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }\n .dark-context { background: #111827; padding: 16px; border-radius: 12px; }\n .dark-context + .dark-context { margin-top: 12px; }\n /* Card variants: explicit dark vs light pairings, both WCAG AA compliant */\n .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }\n .card h4 { font-weight: 600; font-size: 13px; margin: 0; }\n .card p { font-size: 12px; margin: 4px 0 0; }\n .card-dark { background: #1f2937; }\n .card-dark h4 { color: #f9fafb; }\n .card-dark p { color: #cbd5e1; }\n .card-light { background: white; }\n .card-light h4 { color: #0f172a; }\n .card-light p { color: #475569; }\n\n /* ── FLAG: dark background + colored glow ── */\n .glow-blue { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }\n .glow-purple { box-shadow: 0 0 25px rgba(139, 92, 246, 0.35); }\n .glow-cyan { box-shadow: 0 0 15px rgba(6, 182, 212, 0.5); }\n .glow-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 0 30px rgba(168, 85, 247, 0.3); }\n\n /* ── FLAG: modern color formats, var() refs, text-shadow ── */\n :root { --fixture-glow: oklch(0.78 0.14 155); }\n .glow-oklch { box-shadow: 0 0 18px oklch(0.72 0.15 195 / 0.5); }\n .glow-hex { box-shadow: 0 0 16px #3b82f6; }\n .glow-hsl { box-shadow: 0 0 22px hsl(280, 80%, 60%); }\n .glow-var { box-shadow: 0 0 10px var(--fixture-glow); }\n .glow-text h4 { text-shadow: 0 0 12px #22d3ee; }\n /* Zero-offset chromatic halo is slop on light backgrounds too */\n .glow-light-oklch { box-shadow: 0 0 20px oklch(0.65 0.2 300 / 0.45); }\n /* Unreadable surface: url() image ancestor. The zero-offset halo tell\n holds on ANY background, so an unknown surface must not suppress it.\n Both element loops must agree here (the browser adapter once returned\n [] for the whole element on an unresolved surface). */\n .photo-context { background-image: url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-halo { box-shadow: 0 0 26px rgba(217, 70, 239, 0.5); }\n /* Opaque gradient atop a url() layer: every stop is opaque, so the\n gradient provably covers the image — the surface IS the dark\n gradient, and the dark-background glow tell may score against it\n (browser hunt; the static loop has no gradient hunt). */\n .photo-opaque-grad { background: linear-gradient(#111827, #0b1220), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .glow-photo-opaque { box-shadow: 0 6px 22px rgba(249, 115, 22, 0.5); }\n\n /* ── PASS: same dark/light backgrounds, but neutral or no glow ── */\n .light-colored-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.15); }\n .dark-normal-shadow { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); }\n .dark-focus-ring { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); }\n .dark-subtle-shadow { box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2); }\n .dark-elevated-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); }\n .dark-elevated-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); }\n .dark-elevated-lg { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); }\n .dark-elevated-inset { box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); }\n .dark-elevated-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); }\n .elevated-sm-light { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }\n .elevated-md-light { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }\n .elevated-warm-light { box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.15); }\n /* Medium-gray card: lighter than dark mode, used to demonstrate \"not dark enough\" for glow detection. White text for AA contrast. */\n .card-medium { background: #6b7280; }\n .card-medium h4 { color: #ffffff; }\n .card-medium p { color: #ffffff; }\n .medium-gray-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3); }\n /* Offset chromatic drop shadow on a light background: a real drop\n shadow, not a halo — must NOT flag under the zero-offset rule.\n (Element path; the page-level text scan caps at one finding.) */\n .light-offset-colored { box-shadow: 0 8px 20px rgba(59, 130, 246, 0.25); }\n /* Achromatic zero-offset shadow: soft ambient elevation, stays legal */\n .light-soft-neutral { box-shadow: 0 0 24px rgba(0, 0, 0, 0.15); }\n /* Offset chromatic shadow under the same unreadable url() surface:\n the dark-background glow tell needs a surface we can read — abstain. */\n .photo-offset-colored { box-shadow: 0 6px 20px rgba(16, 185, 129, 0.35); }\n /* Translucent gradient atop a url() layer: the image shows through the\n 20% wash, so the real surface is a blend with unknowable pixels (a\n white photo composites to ~#cccccc, not black). Averaging the wash\n stops as if they were the surface scored this \"dark\" — abstain. */\n .photo-translucent-grad { background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/fixtures/antipatterns/missing-photo.png'); padding: 16px; border-radius: 12px; }\n .photo-translucent-offset { box-shadow: 0 6px 22px rgba(244, 63, 94, 0.45); }\n /* Offset neutral text-shadow on dark card: legibility aid, not a glow */\n .dark-text-offset h4 { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); }\n \nstyle=\"box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);\"","classText":"grid\ncol\ndark-context\ncard card-dark glow-blue\ncard card-dark glow-purple\ncard card-dark glow-cyan\ncard card-dark glow-multi\ndark-context\ncard card-dark\ndark-context\ncard card-dark glow-oklch\ncard card-dark glow-hex\ncard card-dark glow-hsl\ncard card-dark glow-var\ncard card-dark glow-text\ncard card-light glow-light-oklch\nphoto-context\ncard card-light glow-photo-halo\nphoto-opaque-grad\ncard card-dark glow-photo-opaque\ncol\ncard card-light light-colored-shadow\ndark-context\ncard card-dark dark-normal-shadow\ncard card-dark dark-focus-ring\ncard card-dark dark-subtle-shadow\ncard card-dark dark-elevated-sm\ncard card-dark dark-elevated-md\ncard card-dark dark-elevated-lg\ncard card-dark dark-elevated-inset\ncard card-dark dark-elevated-multi\ncard card-medium medium-gray-shadow\ncard card-light light-offset-colored\ncard card-light light-soft-neutral\nphoto-context\ncard card-light photo-offset-colored\nphoto-translucent-grad\ncard card-light photo-translucent-offset\ndark-context\ncard card-dark dark-text-offset\ncard card-light elevated-sm-light\ncard card-light elevated-md-light\ncard card-light elevated-warm-light"}],"result":[{"id":"dark-glow","snippet":"Zero-offset box-shadow glow (#3b82f6)","selector":"/* ── FLAG: dark background + colored glow ── */ .glow-blue"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Flush Against Border; Should Flag vs Should Pass</title>\n <style>\n /* Fixture for the flush-against-border rule.\n\n The rule is more general than its name suggests: it fires when a\n container has any visible boundary (border, outline, OR a\n non-transparent background) AND zero inline padding on the\n bounded side, AND text-bearing children land flush against that\n boundary.\n\n Distinct from cramped-padding (which fires when an element has\n its OWN direct text and the padding/font-size ratio is wrong).\n Here the container has NO direct text; only children; so\n cramped-padding gives it a pass. */\n\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 22px; font-weight: 700; margin: 0 0 16px; color: #0f172a; line-height: 1.2; }\n .col h3 { font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; max-width: 60ch; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD FLAG\n ──────────────────────────────────────────────────────────── */\n\n /* ── FLAG: the /live-mode-frameworks bug; border + zero side-padding ── */\n .flag-frameworks {\n padding: 28px 0 0;\n background: #fffbe6;\n border: 1px solid #d4a017;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .flag-frameworks-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #8b6914;\n }\n .flag-frameworks-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .flag-frameworks-list li {\n font-family: ui-monospace, monospace;\n font-size: 13px;\n padding: 6px 12px;\n background: #fef3c7;\n border: 1px solid #d4a017;\n border-radius: 4px;\n color: #78350f;\n }\n\n /* ── FLAG: child text flush against the four borders of a card ── */\n .flag-card-borders {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .flag-card-borders h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-card-borders p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: non-transparent background, zero padding ── */\n .flag-bg-only {\n padding: 0;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .flag-bg-only h4 { margin: 0; font-size: 14px; color: #78350f; }\n .flag-bg-only p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── FLAG: outline (no border), zero padding ── */\n .flag-outline-only {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n }\n .flag-outline-only h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-outline-only p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: border-left + zero left-padding, other sides fine ── */\n .flag-asym-leftflush {\n padding: 12px 16px 12px 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 4px;\n }\n .flag-asym-leftflush h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-asym-leftflush p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD PASS\n ──────────────────────────────────────────────────────────── */\n\n /* ── PASS: no boundary at all (no bg, no border, no outline) ── */\n .pass-no-boundary {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 0;\n }\n .pass-no-boundary-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-no-boundary-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-no-boundary-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: top-only border, no left/right border, padding-top fine ── */\n .pass-top-rule {\n padding: 24px 0 0;\n background: transparent;\n border: 0;\n border-top: 1px solid #cbd5e1;\n }\n .pass-top-rule-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-top-rule-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-top-rule-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: bordered card with comfortable inset on all sides ── */\n .pass-bordered-padded {\n padding: 18px 20px;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-bordered-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-bordered-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: background card with comfortable inset ── */\n .pass-bg-padded {\n padding: 14px 18px;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .pass-bg-padded h4 { margin: 0; font-size: 14px; color: #78350f; }\n .pass-bg-padded p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── PASS: outline with comfortable inset ── */\n .pass-outline-padded {\n padding: 14px 18px;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n border: 0;\n background: transparent;\n }\n .pass-outline-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-outline-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: image-only container, no text means no flush issue ── */\n .pass-image-only {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n width: 200px;\n overflow: hidden;\n }\n .pass-image-only .placeholder-img {\n width: 100%;\n height: 80px;\n background: linear-gradient(135deg, #cbd5e1, #94a3b8);\n }\n\n /* ── PASS: parent has zero padding, but child margins create the visible inset ── */\n .pass-margin-inset {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-margin-inset h4 { margin: 16px 18px 4px; font-size: 14px; color: #0f172a; }\n .pass-margin-inset p { margin: 0 18px 16px; font-size: 13px; color: #475569; }\n\n /* ── PASS: an inner shell, not the parent, owns the inset spacing ── */\n .pass-inner-shell {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-inner-shell .inner-shell {\n margin: 18px;\n }\n .pass-inner-shell h4 { margin: 0 0 4px; font-size: 14px; color: #0f172a; }\n .pass-inner-shell p { margin: 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: same background as parent, so the child draws no visible boundary ── */\n .pass-same-bg-parent {\n padding: 16px;\n background: #eef2f7;\n border-radius: 8px;\n }\n .pass-same-bg-child {\n padding: 0;\n background: #eef2f7;\n border: 0;\n border-radius: 8px;\n }\n .pass-same-bg-child h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-same-bg-child p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: full-bleed marquee/surface, not a text card missing padding ── */\n .pass-marquee-shell {\n padding: 0;\n background: #172033;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-marquee-track {\n display: flex;\n gap: 12px;\n width: max-content;\n }\n .pass-marquee-card {\n width: 220px;\n padding: 14px;\n background: #23304a;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-marquee-card p { margin: 0; font-size: 13px; }\n\n /* ── PASS: outer scene owns background; inner text surface owns padding ── */\n .pass-inner-text-surface {\n padding: 0;\n background: #0f172a;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-inner-text-surface-window {\n display: grid;\n gap: 6px;\n min-height: 92px;\n padding: 14px;\n background: #111827;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-inner-text-surface-window p { margin: 0; font-size: 13px; }\n </style>\n</head>\n<body>\n <div class=\"grid\">\n\n <!-- ════════════════════════════════════════════════════════════\n LEFT COLUMN: should flag\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"flag\">\n <h2>Should flag</h2>\n\n <h3>Border + zero side-padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">section: full border, padding 28px 0 0; label and pills touch the left/right border lines.</span>\n <section class=\"flag-frameworks\">\n <span class=\"flag-frameworks-label\">Supported dev servers</span>\n <ul class=\"flag-frameworks-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n <li>Astro</li>\n </ul>\n </section>\n </div>\n\n <h3>All sides flush against a border</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: 1px border on all sides, padding 0; heading and body touch every side.</span>\n <div class=\"flag-card-borders\">\n <h4>Card flush against the borders</h4>\n <p>Body copy sits flush against every visible border line.</p>\n </div>\n </div>\n\n <h3>Background color, zero padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">no border, but a non-transparent bg; text flush against the bg's edge counts too.</span>\n <div class=\"flag-bg-only\">\n <h4>Title flush against the bg edge</h4>\n <p>Body copy with zero padding around a colored fill.</p>\n </div>\n </div>\n\n <h3>Outline (not border), zero padding</h3>\n <div class=\"case\">\n <span class=\"case-label\">outline draws outside the box; content at the box edge sits next to the outline.</span>\n <div class=\"flag-outline-only\">\n <h4>Title flush against the outline</h4>\n <p>The outline counts as a visible boundary too.</p>\n </div>\n </div>\n\n <h3>Left-side flush, other sides fine</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: padding 12px 16px 12px 0; text touches the left border, other sides are inset.</span>\n <div class=\"flag-asym-leftflush\">\n <h4>Title flush only on the left</h4>\n <p>Padding-left zero with a full border.</p>\n </div>\n </div>\n </div>\n\n <!-- ════════════════════════════════════════════════════════════\n RIGHT COLUMN: should pass\n ═══════════════════════════════════════════════════════════ -->\n <div class=\"col\" data-col=\"pass\">\n <h2>Should pass</h2>\n\n <h3>No boundary at all</h3>\n <div class=\"case\">\n <span class=\"case-label\">no bg, no border, no outline; zero padding is fine.</span>\n <section class=\"pass-no-boundary\">\n <span class=\"pass-no-boundary-label\">Supported dev servers</span>\n <ul class=\"pass-no-boundary-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n </ul>\n </section>\n </div>\n\n <h3>Top-only border, no side borders</h3>\n <div class=\"case\">\n <span class=\"case-label\">border-top only, padding-top 24px; no left/right border to be flush against.</span>\n <section class=\"pass-top-rule\">\n <span class=\"pass-top-rule-label\">Supported dev servers</span>\n <ul class=\"pass-top-rule-list\">\n <li>Vite</li>\n <li>Next.js</li>\n <li>SvelteKit</li>\n </ul>\n </section>\n </div>\n\n <h3>Bordered card with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: 1px border + padding 18px 20px; comfortable inset on every side.</span>\n <div class=\"pass-bordered-padded\">\n <h4>Card with proper inset</h4>\n <p>Body copy sits comfortably away from the borders.</p>\n </div>\n </div>\n\n <h3>Background card with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">non-transparent bg + padding 14px 18px; content has room to breathe.</span>\n <div class=\"pass-bg-padded\">\n <h4>Title with inset on bg</h4>\n <p>Body copy is comfortably inset from the bg edge.</p>\n </div>\n </div>\n\n <h3>Outline with proper inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">outline + padding 14px 18px; content is comfortably inside.</span>\n <div class=\"pass-outline-padded\">\n <h4>Title with inset and outline</h4>\n <p>Outline boundary with proper inset works.</p>\n </div>\n </div>\n\n <h3>Image-only container, no text</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: padding 0, border all sides; no text means nothing can be flush.</span>\n <div class=\"pass-image-only\">\n <div class=\"placeholder-img\"></div>\n </div>\n </div>\n\n <h3>Child margins provide the inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: parent padding 0, but text children have margins that create visible inset.</span>\n <div class=\"pass-margin-inset\">\n <h4>Title inset by child margin</h4>\n <p>Body copy is not visually flush even though the parent has zero padding.</p>\n </div>\n </div>\n\n <h3>Inner shell provides the inset</h3>\n <div class=\"case\">\n <span class=\"case-label\">card: parent padding 0, but an inner shell is offset from every edge.</span>\n <div class=\"pass-inner-shell\">\n <div class=\"inner-shell\">\n <h4>Title inset by inner shell</h4>\n <p>The direct child creates the breathing room.</p>\n </div>\n </div>\n </div>\n\n <h3>Same background as parent</h3>\n <div class=\"case\">\n <span class=\"case-label\">child padding 0, but it has the same background as the parent, so no child boundary is visible.</span>\n <div class=\"pass-same-bg-parent\">\n <div class=\"pass-same-bg-child\">\n <h4>Same-surface visual shell</h4>\n <p>The text belongs to the parent surface, not to a distinct zero-padding card.</p>\n </div>\n </div>\n </div>\n\n <h3>Full-bleed marquee shell</h3>\n <div class=\"case\">\n <span class=\"case-label\">marquee shell clips animated cards intentionally; the cards, not the shell, own text padding.</span>\n <section class=\"pass-marquee-shell\">\n <div class=\"pass-marquee-track\">\n <article class=\"pass-marquee-card\"><p>Quote card with its own comfortable padding.</p></article>\n <article class=\"pass-marquee-card\"><p>Another moving card, also padded inside.</p></article>\n </div>\n </section>\n </div>\n\n <h3>Inner text surface</h3>\n <div class=\"case\">\n <span class=\"case-label\">outer shell has a scene background; the inner window owns the actual text padding.</span>\n <div class=\"pass-inner-text-surface\">\n <div class=\"pass-inner-text-surface-window\">\n <p>Build step running</p>\n <p>Detector output belongs to the mock window.</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n <script src=\"/js/detect-antipatterns-browser.js\"></script>\n</body>\n</html>\n",{"styleText":"\n /* Fixture for the flush-against-border rule.\n\n The rule is more general than its name suggests: it fires when a\n container has any visible boundary (border, outline, OR a\n non-transparent background) AND zero inline padding on the\n bounded side, AND text-bearing children land flush against that\n boundary.\n\n Distinct from cramped-padding (which fires when an element has\n its OWN direct text and the padding/font-size ratio is wrong).\n Here the container has NO direct text; only children; so\n cramped-padding gives it a pass. */\n\n body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }\n .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }\n .col h2 { font-size: 22px; font-weight: 700; margin: 0 0 16px; color: #0f172a; line-height: 1.2; }\n .col h3 { font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; margin: 28px 0 10px; color: #64748b; }\n .case { margin-bottom: 14px; }\n .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; max-width: 60ch; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD FLAG\n ──────────────────────────────────────────────────────────── */\n\n /* ── FLAG: the /live-mode-frameworks bug; border + zero side-padding ── */\n .flag-frameworks {\n padding: 28px 0 0;\n background: #fffbe6;\n border: 1px solid #d4a017;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .flag-frameworks-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #8b6914;\n }\n .flag-frameworks-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .flag-frameworks-list li {\n font-family: ui-monospace, monospace;\n font-size: 13px;\n padding: 6px 12px;\n background: #fef3c7;\n border: 1px solid #d4a017;\n border-radius: 4px;\n color: #78350f;\n }\n\n /* ── FLAG: child text flush against the four borders of a card ── */\n .flag-card-borders {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .flag-card-borders h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-card-borders p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: non-transparent background, zero padding ── */\n .flag-bg-only {\n padding: 0;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .flag-bg-only h4 { margin: 0; font-size: 14px; color: #78350f; }\n .flag-bg-only p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── FLAG: outline (no border), zero padding ── */\n .flag-outline-only {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n }\n .flag-outline-only h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-outline-only p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── FLAG: border-left + zero left-padding, other sides fine ── */\n .flag-asym-leftflush {\n padding: 12px 16px 12px 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 4px;\n }\n .flag-asym-leftflush h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .flag-asym-leftflush p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ────────────────────────────────────────────────────────────\n SHOULD PASS\n ──────────────────────────────────────────────────────────── */\n\n /* ── PASS: no boundary at all (no bg, no border, no outline) ── */\n .pass-no-boundary {\n padding: 0;\n background: transparent;\n border: 0;\n outline: 0;\n }\n .pass-no-boundary-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-no-boundary-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-no-boundary-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: top-only border, no left/right border, padding-top fine ── */\n .pass-top-rule {\n padding: 24px 0 0;\n background: transparent;\n border: 0;\n border-top: 1px solid #cbd5e1;\n }\n .pass-top-rule-label {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: #475569;\n margin-bottom: 10px;\n }\n .pass-top-rule-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .pass-top-rule-list li {\n font-size: 13px;\n padding: 6px 12px;\n background: #f5f5f7;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n color: #0f172a;\n }\n\n /* ── PASS: bordered card with comfortable inset on all sides ── */\n .pass-bordered-padded {\n padding: 18px 20px;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-bordered-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-bordered-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: background card with comfortable inset ── */\n .pass-bg-padded {\n padding: 14px 18px;\n background: #fde68a;\n border-radius: 6px;\n border: 0;\n }\n .pass-bg-padded h4 { margin: 0; font-size: 14px; color: #78350f; }\n .pass-bg-padded p { margin: 4px 0 0; font-size: 13px; color: #78350f; }\n\n /* ── PASS: outline with comfortable inset ── */\n .pass-outline-padded {\n padding: 14px 18px;\n outline: 2px solid #94a3b8;\n outline-offset: 0;\n border: 0;\n background: transparent;\n }\n .pass-outline-padded h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-outline-padded p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: image-only container, no text means no flush issue ── */\n .pass-image-only {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n width: 200px;\n overflow: hidden;\n }\n .pass-image-only .placeholder-img {\n width: 100%;\n height: 80px;\n background: linear-gradient(135deg, #cbd5e1, #94a3b8);\n }\n\n /* ── PASS: parent has zero padding, but child margins create the visible inset ── */\n .pass-margin-inset {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-margin-inset h4 { margin: 16px 18px 4px; font-size: 14px; color: #0f172a; }\n .pass-margin-inset p { margin: 0 18px 16px; font-size: 13px; color: #475569; }\n\n /* ── PASS: an inner shell, not the parent, owns the inset spacing ── */\n .pass-inner-shell {\n padding: 0;\n background: white;\n border: 1px solid #cbd5e1;\n border-radius: 8px;\n }\n .pass-inner-shell .inner-shell {\n margin: 18px;\n }\n .pass-inner-shell h4 { margin: 0 0 4px; font-size: 14px; color: #0f172a; }\n .pass-inner-shell p { margin: 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: same background as parent, so the child draws no visible boundary ── */\n .pass-same-bg-parent {\n padding: 16px;\n background: #eef2f7;\n border-radius: 8px;\n }\n .pass-same-bg-child {\n padding: 0;\n background: #eef2f7;\n border: 0;\n border-radius: 8px;\n }\n .pass-same-bg-child h4 { margin: 0; font-size: 14px; color: #0f172a; }\n .pass-same-bg-child p { margin: 4px 0 0; font-size: 13px; color: #475569; }\n\n /* ── PASS: full-bleed marquee/surface, not a text card missing padding ── */\n .pass-marquee-shell {\n padding: 0;\n background: #172033;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-marquee-track {\n display: flex;\n gap: 12px;\n width: max-content;\n }\n .pass-marquee-card {\n width: 220px;\n padding: 14px;\n background: #23304a;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-marquee-card p { margin: 0; font-size: 13px; }\n\n /* ── PASS: outer scene owns background; inner text surface owns padding ── */\n .pass-inner-text-surface {\n padding: 0;\n background: #0f172a;\n color: white;\n overflow: hidden;\n border: 0;\n }\n .pass-inner-text-surface-window {\n display: grid;\n gap: 6px;\n min-height: 92px;\n padding: 14px;\n background: #111827;\n border: 1px solid rgba(255, 255, 255, 0.16);\n }\n .pass-inner-text-surface-window p { margin: 0; font-size: 13px; }\n ","classText":"grid\ncol\ncase\ncase-label\nflag-frameworks\nflag-frameworks-label\nflag-frameworks-list\ncase\ncase-label\nflag-card-borders\ncase\ncase-label\nflag-bg-only\ncase\ncase-label\nflag-outline-only\ncase\ncase-label\nflag-asym-leftflush\ncol\ncase\ncase-label\npass-no-boundary\npass-no-boundary-label\npass-no-boundary-list\ncase\ncase-label\npass-top-rule\npass-top-rule-label\npass-top-rule-list\ncase\ncase-label\npass-bordered-padded\ncase\ncase-label\npass-bg-padded\ncase\ncase-label\npass-outline-padded\ncase\ncase-label\npass-image-only\nplaceholder-img\ncase\ncase-label\npass-margin-inset\ncase\ncase-label\npass-inner-shell\ninner-shell\ncase\ncase-label\npass-same-bg-parent\npass-same-bg-child\ncase\ncase-label\npass-marquee-shell\npass-marquee-track\npass-marquee-card\npass-marquee-card\ncase\ncase-label\npass-inner-text-surface\npass-inner-text-surface-window"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Oversized H1 fixture</title>\n <style>\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n h1 { margin: 0 0 24px; line-height: 1.05; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: a long headline set at display size, dominating the viewport -->\n <section class=\"col flag\">\n <h1 style=\"font-size: 96px\">A sweeping product headline that runs on far too long to sit at this size</h1>\n <h1 style=\"font-size: 80px\">Everything your whole team needs to launch quickly and finally rest at night</h1>\n </section>\n\n <!-- PASS column: punchy short headlines at display size (a valid choice), and a long headline at a sane size -->\n <section class=\"col pass\">\n <h1 style=\"font-size: 168px\">Bold.</h1>\n <h1 style=\"font-size: 120px\">Ship faster</h1>\n <h1 style=\"font-size: 40px\">A perfectly ordinary headline that is reasonably long but set at a sane, readable size</h1>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n h1 { margin: 0 0 24px; line-height: 1.05; }\n \nstyle=\"font-size: 96px\"\nstyle=\"font-size: 80px\"\nstyle=\"font-size: 168px\"\nstyle=\"font-size: 120px\"\nstyle=\"font-size: 40px\"","classText":"cols\ncol flag\ncol pass"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Extreme negative tracking fixture</title>\n <style>\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n p, h2 { margin: 0 0 20px; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: tracking crushed past the point of legibility -->\n <section class=\"col flag\">\n <p style=\"letter-spacing: -0.06em\">Tracking crushed em paragraph copy that should still read as a sentence.</p>\n <p style=\"letter-spacing: -4px; font-size: 64px\">Tracking crushed pixels heading line</p>\n <h2 style=\"letter-spacing: -0.07em\">Tracking crushed heading variant text</h2>\n </section>\n\n <!-- PASS column: optical tightening that display type legitimately wants -->\n <section class=\"col pass\">\n <h1 style=\"letter-spacing: -0.02em; font-size: 72px\">Optical tighten display</h1>\n <p style=\"letter-spacing: normal\">Plain paragraph copy with default tracking that reads comfortably.</p>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n p, h2 { margin: 0 0 20px; }\n \nstyle=\"letter-spacing: -0.06em\"\nstyle=\"letter-spacing: -4px; font-size: 64px\"\nstyle=\"letter-spacing: -0.07em\"\nstyle=\"letter-spacing: -0.02em; font-size: 72px\"\nstyle=\"letter-spacing: normal\"","classText":"cols\ncol flag\ncol pass"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Clipped overflow container fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .box { position: relative; width: 200px; height: 120px; margin: 0 0 24px; padding: 16px; border: 1px solid #ddd; }\n .pop { position: absolute; top: 100%; left: 0; width: 220px; background: #222; color: #fff; padding: 8px; }\n .tip-negative { position: absolute; top: -34px; left: 16px; width: 160px; background: #222; color: #fff; padding: 8px; }\n .flyout { position: absolute; top: 28px; left: 100%; width: 180px; background: #222; color: #fff; padding: 8px; }\n .contained-badge { position: absolute; top: 10px; right: 10px; width: 36px; height: 24px; background: #e5f0ff; }\n .button-shine { position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); }\n .crop-photo { position: absolute; inset: -18px; background: linear-gradient(135deg, #b7d7ff, #486aa3); }\n .inside-overlay { position: absolute; left: 12px; right: 12px; bottom: 12px; min-height: 28px; background: rgba(255,255,255,.86); }\n .pass-hidden-button { position: relative; overflow: hidden; width: 160px; height: 52px; border: 1px solid #ddd; background: #111; color: #fff; }\n .pass-carousel-viewport,\n .pass-fisheye-list,\n .pass-split-container {\n position: relative;\n overflow: hidden;\n width: 200px;\n height: 120px;\n margin: 0 0 24px;\n border: 1px solid #ddd;\n }\n .carousel-slide-next { position: absolute; top: 100%; left: 0; width: 100%; height: 100%; padding: 16px; background: #f5f5f5; }\n .fisheye-next-item { position: absolute; top: 100%; left: 0; width: 100%; padding: 8px; background: none; border: 0; text-align: left; }\n .split-after-panel { position: absolute; top: 0; left: 50%; width: 100%; height: 100%; background: #f5f5f5; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: clipping container with a positioned child that needs to escape -->\n <section class=\"col flag\">\n <div class=\"box flag-overflow-hidden\" style=\"overflow: hidden\">\n <button>Menu</button>\n <ul class=\"pop\">Dropdown that gets clipped by the hidden ancestor</ul>\n </div>\n <div class=\"box flag-overflow-clip\" style=\"overflow: clip\">\n <button>Tooltip host</button>\n <span class=\"pop\">Tooltip clipped by overflow clip</span>\n </div>\n <div class=\"box flag-overflow-negative\" style=\"overflow: hidden\">\n <button>Help</button>\n <span class=\"tip-negative\">Tooltip clipped above the host</span>\n </div>\n <div class=\"box flag-overflow-right\" style=\"overflow: hidden\">\n <button>Actions</button>\n <div class=\"flyout\">Flyout menu clipped to the right of the host</div>\n </div>\n <div class=\"box flag-shadow-utility\" style=\"overflow: hidden\">\n <button>More</button>\n <div class=\"pop bg-white shadow-lg\">Dropdown with utility classes still gets clipped.</div>\n </div>\n <div class=\"box flag-overlay-surface\" style=\"overflow: clip\">\n <button>Dialog</button>\n <div class=\"pop modal-overlay\">Overlay-style dialog content still needs to escape.</div>\n </div>\n </section>\n\n <!-- PASS column: no clipping, no positioned child, real scroll region, or contained decorative positioning -->\n <section class=\"col pass\">\n <div class=\"box pass-hidden-no-abs\" style=\"overflow: hidden\">\n <p style=\"position: static\">Hidden container with only static content.</p>\n </div>\n <div class=\"box pass-visible-abs\" style=\"overflow: visible\">\n <button>Visible host</button>\n <span class=\"pop\">Positioned child can escape because overflow is visible.</span>\n </div>\n <div class=\"box pass-scroll-abs\" style=\"overflow: auto\">\n <button>Scroll host</button>\n <span class=\"pop\">A genuine scroll region is allowed to contain positioned children.</span>\n </div>\n <div class=\"box pass-contained-abs\" style=\"overflow: hidden\">\n <p>Positioned decoration remains inside the clipping box.</p>\n <span class=\"contained-badge\" aria-hidden=\"true\"></span>\n </div>\n <button type=\"button\" class=\"pass-hidden-button pass-button-shine\">\n Save\n <span class=\"button-shine\" aria-hidden=\"true\"></span>\n </button>\n <div class=\"box pass-crop-photo\" style=\"overflow: hidden\">\n <span class=\"crop-photo\" aria-hidden=\"true\"></span>\n </div>\n <div class=\"box pass-contained-overlay\" style=\"overflow: clip\">\n <p>Mockup frame with a contained absolute overlay.</p>\n <span class=\"inside-overlay\" aria-hidden=\"true\"></span>\n </div>\n <div class=\"pass-carousel-viewport\" aria-roledescription=\"carousel\">\n <div class=\"carousel-slide-next\">A hidden next slide is clipped intentionally by the viewport.</div>\n </div>\n <div class=\"pass-fisheye-list\">\n <button type=\"button\" class=\"fisheye-next-item\">Next command</button>\n </div>\n <div class=\"pass-split-container\">\n <div class=\"split-after-panel\">Before/after comparison panel clipped by the split frame.</div>\n </div>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .box { position: relative; width: 200px; height: 120px; margin: 0 0 24px; padding: 16px; border: 1px solid #ddd; }\n .pop { position: absolute; top: 100%; left: 0; width: 220px; background: #222; color: #fff; padding: 8px; }\n .tip-negative { position: absolute; top: -34px; left: 16px; width: 160px; background: #222; color: #fff; padding: 8px; }\n .flyout { position: absolute; top: 28px; left: 100%; width: 180px; background: #222; color: #fff; padding: 8px; }\n .contained-badge { position: absolute; top: 10px; right: 10px; width: 36px; height: 24px; background: #e5f0ff; }\n .button-shine { position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); }\n .crop-photo { position: absolute; inset: -18px; background: linear-gradient(135deg, #b7d7ff, #486aa3); }\n .inside-overlay { position: absolute; left: 12px; right: 12px; bottom: 12px; min-height: 28px; background: rgba(255,255,255,.86); }\n .pass-hidden-button { position: relative; overflow: hidden; width: 160px; height: 52px; border: 1px solid #ddd; background: #111; color: #fff; }\n .pass-carousel-viewport,\n .pass-fisheye-list,\n .pass-split-container {\n position: relative;\n overflow: hidden;\n width: 200px;\n height: 120px;\n margin: 0 0 24px;\n border: 1px solid #ddd;\n }\n .carousel-slide-next { position: absolute; top: 100%; left: 0; width: 100%; height: 100%; padding: 16px; background: #f5f5f5; }\n .fisheye-next-item { position: absolute; top: 100%; left: 0; width: 100%; padding: 8px; background: none; border: 0; text-align: left; }\n .split-after-panel { position: absolute; top: 0; left: 50%; width: 100%; height: 100%; background: #f5f5f5; }\n \nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"\nstyle=\"overflow: hidden\"\nstyle=\"position: static\"\nstyle=\"overflow: visible\"\nstyle=\"overflow: auto\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: hidden\"\nstyle=\"overflow: clip\"","classText":"cols\ncol flag\nbox flag-overflow-hidden\npop\nbox flag-overflow-clip\npop\nbox flag-overflow-negative\ntip-negative\nbox flag-overflow-right\nflyout\nbox flag-shadow-utility\npop bg-white shadow-lg\nbox flag-overlay-surface\npop modal-overlay\ncol pass\nbox pass-hidden-no-abs\nbox pass-visible-abs\npop\nbox pass-scroll-abs\npop\nbox pass-contained-abs\ncontained-badge\npass-hidden-button pass-button-shine\nbutton-shine\nbox pass-crop-photo\ncrop-photo\nbox pass-contained-overlay\ninside-overlay\npass-carousel-viewport\ncarousel-slide-next\npass-fisheye-list\nfisheye-next-item\npass-split-container\nsplit-after-panel"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Cream palette fixture</title>\n <style>\n body { background: #f5efe2; color: #2a2622; font-family: Georgia, serif; margin: 0; padding: 40px; }\n h1 { font-size: 40px; margin: 0 0 16px; }\n p { max-width: 60ch; line-height: 1.6; }\n </style>\n</head>\n<body>\n <h1>A warm cream surface</h1>\n <p>The whole page sits on the default tasteful off-white that every generated landing page reaches for. The background is the tell, regardless of what sits on top of it.</p>\n</body>\n</html>\n",{"styleText":"\n body { background: #f5efe2; color: #2a2622; font-family: Georgia, serif; margin: 0; padding: 40px; }\n h1 { font-size: 40px; margin: 0 0 16px; }\n p { max-width: 60ch; line-height: 1.6; }\n ","classText":""}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Cream palette (Tailwind) fixture</title>\n</head>\n<body class=\"bg-amber-50 text-stone-800 font-serif\">\n <h1>A warm cream surface via Tailwind</h1>\n <p>The background comes from a utility class (bg-amber-50), not inline CSS, so the static engine never resolves it to a computed color. The class-list fallback should still catch it.</p>\n</body>\n</html>\n",{"styleText":"","classText":"bg-amber-50 text-stone-800 font-serif"}],"result":[]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Recurring generated-UI tell fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .card { padding: 24px; margin: 0 0 24px; border-radius: 12px; }\n .stripes { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n .grid { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: recurring generated-UI idioms -->\n <section class=\"col flag\">\n <div class=\"card flag-thin-border-wide-shadow\" style=\"border: 1px solid #e5e7eb; box-shadow: 0 0 24px rgba(0,0,0,0.18)\">\n Thin hairline border paired with a wide diffuse shadow.\n </div>\n <div class=\"stripes flag-repeating-stripes\" style=\"background: repeating-linear-gradient(45deg, #eee, #eee 10px, #fafafa 10px, #fafafa 20px)\"></div>\n <div class=\"grid flag-codex-grid\" style=\"background: linear-gradient(90deg, oklch(96% 0.012 82 / 0.055) 1px, transparent 1px), linear-gradient(180deg, oklch(96% 0.012 82 / 0.05) 1px, transparent 1px); background-size: 78px 78px, 78px 78px;\"></div>\n <p class=\"flag-theater\">We retired the growth theater and shipped something that actually works.</p>\n </section>\n\n <!-- PASS column: the legitimate, non-tell versions -->\n <section class=\"col pass\">\n <div class=\"card pass-tight-shadow\" style=\"border: 1px solid #e5e7eb; box-shadow: 0 1px 4px rgba(0,0,0,0.08)\">\n Hairline border with a tight, purposeful shadow.\n </div>\n <div class=\"card pass-soft-border-wide-shadow\" style=\"border: 1px solid rgba(148, 163, 184, 0.16); box-shadow: 0 18px 48px -16px rgba(15, 23, 42, 0.18)\">\n A quiet low-contrast hairline with a broad but restrained ambient shadow.\n </div>\n <div class=\"card pass-oklch-soft-shadow\" style=\"border: 1px solid oklch(78% 0 0 / 0.16); box-shadow: 0 16px 36px -12px oklch(2% 0.004 95 / 0.18)\">\n OKLCH shadow hue values should not be mistaken for shadow blur.\n </div>\n <div class=\"stripes pass-plain-gradient\" style=\"background: linear-gradient(180deg, #f3f4f6, #ffffff)\"></div>\n <div class=\"grid pass-single-axis-rule\" style=\"background: linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 100% 32px\"></div>\n <div class=\"grid pass-two-color-blend\" style=\"background: linear-gradient(90deg, #f3f4f6, #e5e7eb), linear-gradient(180deg, #eef2ff, #ffffff)\"></div>\n <div class=\"grid pass-crosshair-no-tiling\" style=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px), linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px)\"></div>\n <div class=\"grid pass-bg-plus-mask-hairline\" style=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 32px 32px; mask-image: linear-gradient(180deg, #000 1px, transparent 1px)\"></div>\n <p class=\"pass-no-theater\">We measured real outcomes for the people who use this every day.</p>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n .card { padding: 24px; margin: 0 0 24px; border-radius: 12px; }\n .stripes { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n .grid { height: 80px; margin: 0 0 24px; border-radius: 12px; }\n \nstyle=\"border: 1px solid #e5e7eb; box-shadow: 0 0 24px rgba(0,0,0,0.18)\"\nstyle=\"background: repeating-linear-gradient(45deg, #eee, #eee 10px, #fafafa 10px, #fafafa 20px)\"\nstyle=\"background: linear-gradient(90deg, oklch(96% 0.012 82 / 0.055) 1px, transparent 1px), linear-gradient(180deg, oklch(96% 0.012 82 / 0.05) 1px, transparent 1px); background-size: 78px 78px, 78px 78px;\"\nstyle=\"border: 1px solid #e5e7eb; box-shadow: 0 1px 4px rgba(0,0,0,0.08)\"\nstyle=\"border: 1px solid rgba(148, 163, 184, 0.16); box-shadow: 0 18px 48px -16px rgba(15, 23, 42, 0.18)\"\nstyle=\"border: 1px solid oklch(78% 0 0 / 0.16); box-shadow: 0 16px 36px -12px oklch(2% 0.004 95 / 0.18)\"\nstyle=\"background: linear-gradient(180deg, #f3f4f6, #ffffff)\"\nstyle=\"background: linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 100% 32px\"\nstyle=\"background: linear-gradient(90deg, #f3f4f6, #e5e7eb), linear-gradient(180deg, #eef2ff, #ffffff)\"\nstyle=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px), linear-gradient(180deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px)\"\nstyle=\"background: linear-gradient(90deg, rgba(15, 23, 42, 0.06) 1px, transparent 1px); background-size: 32px 32px; mask-image: linear-gradient(180deg, #000 1px, transparent 1px)\"","classText":"cols\ncol flag\ncard flag-thin-border-wide-shadow\nstripes flag-repeating-stripes\ngrid flag-codex-grid\nflag-theater\ncol pass\ncard pass-tight-shadow\ncard pass-soft-border-wide-shadow\ncard pass-oklch-soft-shadow\nstripes pass-plain-gradient\ngrid pass-single-axis-rule\ngrid pass-two-color-blend\ngrid pass-crosshair-no-tiling\ngrid pass-bg-plus-mask-hairline\npass-no-theater"}],"result":[{"id":"repeating-stripes-gradient","snippet":"repeating-gradient decorative stripes","selector":".grid"},{"id":"codex-grid-background","snippet":"two-axis grid-line gradient background","selector":".grid"},{"id":"theater-slop-phrase","snippet":"\"growth theater\""}]}
{"args":["<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>Image hover-transform fixture</title>\n <style>\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n img { display: block; width: 160px; height: 120px; margin: 0 0 24px; background: #ddd; }\n .flag-css-hover img { transition: transform 0.3s; }\n .flag-css-hover img:hover { transform: scale(1.05); }\n .card:hover { transform: translateY(-4px); }\n </style>\n</head>\n<body>\n <div class=\"cols\">\n <!-- FLAG column: image hover transforms -->\n <section class=\"col flag\">\n <div class=\"flag-css-hover\">\n <img src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23c9b89a'/%3E%3C/svg%3E\" alt=\"CSS hover-zoom image\" />\n </div>\n <img class=\"flag-tw-hover hover:scale-105 transition-transform\" src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23a89c86'/%3E%3C/svg%3E\" alt=\"Tailwind hover-zoom image\" />\n </section>\n\n <!-- PASS column: static images, and hover transforms on non-image elements -->\n <section class=\"col pass\">\n <img class=\"pass-static\" src=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='120'%3E%3Crect%20width='160'%20height='120'%20fill='%23ddd6c8'/%3E%3C/svg%3E\" alt=\"Static image with no hover transform\" />\n <div class=\"card pass-non-img-hover\">A card that lifts on hover, which is fine because it is not an image.</div>\n </section>\n </div>\n</body>\n</html>\n",{"styleText":"\n body { font-family: system-ui, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }\n .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 24px; }\n .col { padding: 16px; }\n img { display: block; width: 160px; height: 120px; margin: 0 0 24px; background: #ddd; }\n .flag-css-hover img { transition: transform 0.3s; }\n .flag-css-hover img:hover { transform: scale(1.05); }\n .card:hover { transform: translateY(-4px); }\n ","classText":"cols\ncol flag\nflag-css-hover\nflag-tw-hover hover:scale-105 transition-transform\ncol pass\npass-static\ncard pass-non-img-hover"}],"result":[{"id":"image-hover-transform","snippet":"img:hover { transform } rule","selector":".flag-css-hover img:hover"},{"id":"image-hover-transform","snippet":"Tailwind hover transform on <img>"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Em-dash entity overuse</title>\n</head>\n<body>\n <main>\n <h1>A page whose em-dashes hide inside HTML entities</h1>\n <p>\n The product is fast &mdash; it is also cheap &mdash; and it is honest\n &mdash; which matters &mdash; more than speed &mdash; or price\n &mdash; in the short term &mdash; and the long run &mdash; always.\n </p>\n </main>\n</body>\n</html>\n",{"styleText":"","classText":""}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Regression fixture: CSS-property patterns appearing in prose, code samples,\n and comments are documentation ABOUT css, not styling. The page-level\n pattern checks must scan only real style carriers (<style> blocks, style=\"\"\n attribute values, linked stylesheets, class=\"\" attribute values), never\n rendered text. Live false positive that motivated this fixture: the\n impeccable.style changelog line\n \"<code>background-clip: text</code> gradients stop tripping contrast\"\n tripped gradient-text on the raw-source scan, and the purple hex codes in\n its rule documentation shared the same exposure via ai-color-palette.\n\n Every case in this file SHOULD PASS: nothing here styles the page with the\n documented patterns.\n-->\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Docs about CSS anti-patterns</title>\n<style>\n body { font-family: Georgia, 'Iowan Old Style', serif; margin: 0; padding: 32px 24px 40px; color: #1f2933; background: #fdfcf9; }\n h1 { font-size: 34px; margin: 0 0 12px; }\n h2 { font-size: 20px; margin: 28px 0 8px; }\n p { font-size: 16px; max-width: 640px; }\n pre { background: #f1efe9; padding: 16px; overflow-x: auto; font-size: 13px; }\n code { font-family: 'SF Mono', ui-monospace, monospace; }\n</style>\n</head>\n<body>\n<h1>Changelog</h1>\n\n<!-- SHOULD PASS (gradient-text): the exact changelog shape — inline <code>\n naming the property with \"gradient\" prose within the context window. -->\n<h2>Detector</h2>\n<p><code>background-clip: text</code> gradients stop tripping the contrast rules when the glyph fill is transparent.</p>\n\n<!-- SHOULD PASS (gradient-text + ai-color-palette): a <pre> CSS sample\n documenting the pattern, purple hexes and all. -->\n<h2>What the rule catches</h2>\n<pre><code>.hero-title {\n background: linear-gradient(135deg, #667eea, #764ba2);\n -webkit-background-clip: text;\n background-clip: text;\n color: #7c3aed;\n}</code></pre>\n\n<!-- SHOULD PASS (gradient-text, Tailwind form): prose naming the utility\n classes without applying them. -->\n<p>The Tailwind form pairs bg-clip-text with bg-gradient-to-r on one element; the two utilities together are the tell.</p>\n\n<!-- SHOULD PASS (gradient-text): an HTML comment carrying the pattern.\n background-clip: text; with a linear-gradient(90deg, #7c3aed, #a855f7)\n fill is the construction the rule looks for. -->\n\n<!-- SHOULD PASS (pulsing-dot): a <pre> sample of the decorative live dot. -->\n<h2>Pulsing dots</h2>\n<pre><code>.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n animation: pulse 2s ease-in-out infinite;\n}</code></pre>\n\n<!-- SHOULD PASS (side-tab): a <pre> sample of the pseudo-element stripe. -->\n<h2>Side stripes</h2>\n<pre><code>.card::before {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 4px;\n background: #3b82f6;\n}</code></pre>\n\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, 'Iowan Old Style', serif; margin: 0; padding: 32px 24px 40px; color: #1f2933; background: #fdfcf9; }\n h1 { font-size: 34px; margin: 0 0 12px; }\n h2 { font-size: 20px; margin: 28px 0 8px; }\n p { font-size: 16px; max-width: 640px; }\n pre { background: #f1efe9; padding: 16px; overflow-x: auto; font-size: 13px; }\n code { font-family: 'SF Mono', ui-monospace, monospace; }\n","classText":""}],"result":[]}
{"args":["<!DOCTYPE html>\n<!--\n Companion to css-in-prose-should-pass.html: the same patterns applied as\n real styling. The scoped-corpus scan must still catch every carrier a page\n can style itself with — a <style> rule, an inline style=\"\" attribute, and\n Tailwind utility classes.\n-->\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Real gradient-text styling</title>\n<style>\n body { font-family: Georgia, serif; margin: 0; padding: 40px 24px; background: #fdfcf9; color: #1f2933; }\n /* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */\n .gradient-heading {\n background: linear-gradient(135deg, #7c3aed, #a855f7);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n font-size: 44px;\n }\n</style>\n</head>\n<body>\n<h1 class=\"gradient-heading\">Ship Faster With Us</h1>\n\n<!-- SHOULD FLAG (gradient-text): the same construction inline. -->\n<h2 style=\"background: linear-gradient(90deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 28px;\">Inline gradient heading</h2>\n\n<!-- SHOULD FLAG (gradient-text, Tailwind): bg-clip-text + bg-gradient-to- classes. -->\n<h2 class=\"bg-clip-text bg-gradient-to-r from-purple-500 to-pink-500 text-transparent\">Tailwind gradient heading</h2>\n\n<p>Body copy long enough to make this a real page for the scanners to read.</p>\n</body>\n</html>\n",{"styleText":"\n body { font-family: Georgia, serif; margin: 0; padding: 40px 24px; background: #fdfcf9; color: #1f2933; }\n /* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */\n .gradient-heading {\n background: linear-gradient(135deg, #7c3aed, #a855f7);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n font-size: 44px;\n }\n\nstyle=\"background: linear-gradient(90deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 28px;\"","classText":"gradient-heading\nbg-clip-text bg-gradient-to-r from-purple-500 to-pink-500 text-transparent"}],"result":[{"id":"ai-color-palette","snippet":"Purple/violet accent colors detected","selector":"/* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */ .gradient-heading"},{"id":"gradient-text","snippet":"background-clip: text + gradient","selector":"/* SHOULD FLAG (gradient-text + ai-color-palette): a real style rule. */ .gradient-heading"},{"id":"gradient-text","snippet":"bg-clip-text + bg-gradient (Tailwind)"}]}
{"args":["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Dark theme, modern color syntax</title>\n<style>\n /* A lacquer-black ground painted as a gradient in oklch, the shape that\n floods dark themes with \"on #ffffff\" low-contrast false positives when\n the detector cannot read the ground and assumes white. */\n body {\n margin: 0;\n padding: 24px;\n background: linear-gradient(oklch(0.07 0.006 95), oklch(0.11 0.008 95));\n font-family: Georgia, serif;\n font-size: 15px;\n line-height: 1.6;\n }\n\n section {\n padding: 24px;\n margin-bottom: 24px;\n width: 640px;\n }\n\n p { margin: 0 0 12px; font-size: 15px; }\n\n /* Readable grounds, each written in a color syntax a real browser keeps\n verbatim in getComputedStyle output. */\n .panel-srgb { background-color: color(srgb 0.1 0.11 0.12); }\n .panel-mix { background-color: color-mix(in srgb, white 6%, #0b0b0d); }\n .panel-p3 { background-color: color(display-p3 0.07 0.07 0.08); }\n .panel-lch { background-color: lch(18 4 60); }\n .panel-light { background-color: color(srgb 0.98 0.97 0.95); }\n\n /* A color space the detector does not model. Nested inside the light\n panel so a detector that walks PAST the unreadable layer would measure\n light text against the light section and invent a finding. */\n .panel-unreadable { background-color: color(rec2020 0.05 0.05 0.05); padding: 20px; }\n\n /* An image layer stacked ABOVE a loud gradient. The image is the visible\n surface and its pixels are unreadable; a detector that falls back to\n gradient stops here measures a layer the visitor never sees. */\n .panel-image-over-gradient { background-image: url(\"opaque-panel.png\"), linear-gradient(#ff0000, #0000ff); }\n\n /* Keywords a real browser resolves before getComputedStyle output but a\n partial cascade hands through verbatim: inherit takes the parent's\n ground, currentcolor paints with the element's own text color. Both are\n knowable surfaces; abstaining on them would hide real findings. */\n .panel-inherit { background-color: inherit; padding: 20px; }\n .panel-current { background-color: currentcolor; color: color(srgb 0.94 0.93 0.91); }\n .text-pale-2 { color: color(srgb 0.78 0.77 0.75); }\n\n .text-light { color: oklch(0.92 0.01 90); }\n .text-dark { color: color(srgb 0.1 0.11 0.12); }\n\n .text-muted-ground { color: oklch(0.32 0.01 90); }\n .text-dim-srgb { color: color(srgb 0.28 0.28 0.3); }\n .text-dim-p3 { color: color(display-p3 0.35 0.35 0.36); }\n .text-dim-lch { color: lch(35 3 60); }\n .text-pale-light { color: color(srgb 0.75 0.74 0.72); }\n</style>\n</head>\n<body>\n\n<h1 class=\"text-light\">Should flag</h1>\n\n<section>\n <p class=\"text-muted-ground\">Flag Muted On Oklch Ground: charcoal body copy sitting straight on the lacquer gradient.</p>\n</section>\n\n<section class=\"panel-srgb\">\n <p class=\"text-dim-srgb\">Flag Dim On Srgb Panel: slate copy on a near-black srgb surface.</p>\n</section>\n\n<section class=\"panel-p3\">\n <p class=\"text-dim-p3\">Flag Dim On Display P3 Panel: slate copy on a near-black display-p3 surface.</p>\n</section>\n\n<section class=\"panel-lch\">\n <p class=\"text-dim-lch\">Flag Dim On Lch Panel: warm gray copy on a warm near-black lch surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <p class=\"text-pale-light\">Flag Pale On Light Panel: bone copy on a bone-white srgb surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <div class=\"panel-inherit\">\n <p class=\"text-pale-2\">Flag Pale On Inherited Light Panel: the ground is inherited from the light section, so the walk must resolve it rather than abstain.</p>\n </div>\n</section>\n\n<section class=\"panel-current\">\n <p class=\"text-pale-light\">Flag Pale On Currentcolor Panel: the ground is the panel's own bone text color, so the keyword must resolve rather than abstain.</p>\n</section>\n\n<h1 class=\"text-light\">Should pass</h1>\n\n<section>\n <p class=\"text-light\">Pass Light Text On Oklch Gradient Ground: warm ivory copy on the lacquer gradient, roughly 15:1.</p>\n</section>\n\n<section class=\"panel-srgb\">\n <p class=\"text-light\">Pass Light Text On Srgb Panel: warm ivory copy on the near-black srgb surface.</p>\n</section>\n\n<section class=\"panel-mix\">\n <p class=\"text-light\">Pass Light Text On Color Mix Panel: warm ivory copy on a surface a browser reports as color(srgb ...).</p>\n</section>\n\n<section class=\"panel-p3\">\n <p class=\"text-light\">Pass Light Text On Display P3 Panel: warm ivory copy on the near-black display-p3 surface.</p>\n</section>\n\n<section class=\"panel-lch\">\n <p class=\"text-light\">Pass Light Text On Lch Panel: warm ivory copy on the warm near-black lch surface.</p>\n</section>\n\n<section class=\"panel-light\">\n <p class=\"text-dark\">Pass Dark Text On Light Panel: ink copy on the bone-white srgb surface.</p>\n <div class=\"panel-unreadable\">\n <p class=\"text-light\">Pass Unreadable Panel Inside Light Section: the ground is a color space the detector cannot read, so it must abstain instead of measuring against the light section or against white.</p>\n </div>\n</section>\n\n<section class=\"panel-image-over-gradient\">\n <p class=\"text-muted-ground\">Pass Image Over Gradient: the visible ground is an image whose pixels the engine cannot read, so it must abstain instead of measuring the loud gradient hidden beneath it.</p>\n</section>\n\n</body>\n</html>\n",{"styleText":"\n /* A lacquer-black ground painted as a gradient in oklch, the shape that\n floods dark themes with \"on #ffffff\" low-contrast false positives when\n the detector cannot read the ground and assumes white. */\n body {\n margin: 0;\n padding: 24px;\n background: linear-gradient(oklch(0.07 0.006 95), oklch(0.11 0.008 95));\n font-family: Georgia, serif;\n font-size: 15px;\n line-height: 1.6;\n }\n\n section {\n padding: 24px;\n margin-bottom: 24px;\n width: 640px;\n }\n\n p { margin: 0 0 12px; font-size: 15px; }\n\n /* Readable grounds, each written in a color syntax a real browser keeps\n verbatim in getComputedStyle output. */\n .panel-srgb { background-color: color(srgb 0.1 0.11 0.12); }\n .panel-mix { background-color: color-mix(in srgb, white 6%, #0b0b0d); }\n .panel-p3 { background-color: color(display-p3 0.07 0.07 0.08); }\n .panel-lch { background-color: lch(18 4 60); }\n .panel-light { background-color: color(srgb 0.98 0.97 0.95); }\n\n /* A color space the detector does not model. Nested inside the light\n panel so a detector that walks PAST the unreadable layer would measure\n light text against the light section and invent a finding. */\n .panel-unreadable { background-color: color(rec2020 0.05 0.05 0.05); padding: 20px; }\n\n /* An image layer stacked ABOVE a loud gradient. The image is the visible\n surface and its pixels are unreadable; a detector that falls back to\n gradient stops here measures a layer the visitor never sees. */\n .panel-image-over-gradient { background-image: url(\"opaque-panel.png\"), linear-gradient(#ff0000, #0000ff); }\n\n /* Keywords a real browser resolves before getComputedStyle output but a\n partial cascade hands through verbatim: inherit takes the parent's\n ground, currentcolor paints with the element's own text color. Both are\n knowable surfaces; abstaining on them would hide real findings. */\n .panel-inherit { background-color: inherit; padding: 20px; }\n .panel-current { background-color: currentcolor; color: color(srgb 0.94 0.93 0.91); }\n .text-pale-2 { color: color(srgb 0.78 0.77 0.75); }\n\n .text-light { color: oklch(0.92 0.01 90); }\n .text-dark { color: color(srgb 0.1 0.11 0.12); }\n\n .text-muted-ground { color: oklch(0.32 0.01 90); }\n .text-dim-srgb { color: color(srgb 0.28 0.28 0.3); }\n .text-dim-p3 { color: color(display-p3 0.35 0.35 0.36); }\n .text-dim-lch { color: lch(35 3 60); }\n .text-pale-light { color: color(srgb 0.75 0.74 0.72); }\n","classText":"text-light\ntext-muted-ground\npanel-srgb\ntext-dim-srgb\npanel-p3\ntext-dim-p3\npanel-lch\ntext-dim-lch\npanel-light\ntext-pale-light\npanel-light\npanel-inherit\ntext-pale-2\npanel-current\ntext-pale-light\ntext-light\ntext-light\npanel-srgb\ntext-light\npanel-mix\ntext-light\npanel-p3\ntext-light\npanel-lch\ntext-light\npanel-light\ntext-dark\npanel-unreadable\ntext-light\npanel-image-over-gradient\ntext-muted-ground"}],"result":[]}