{
  "version": 1,
  "generated": "2026-11-14T20:11:33.333Z",
  "specimens": [
    {
      "id": "prechecked",
      "url": "/lab/prechecked",
      "pattern": "preselection",
      "title": "Boxes ticked in advance",
      "targets": [
        {
          "selector": "#s1",
          "note": "Checked by default. A paid insurance add-on."
        },
        {
          "selector": "#s3",
          "note": "Checked by default. A newsletter sign-up."
        },
        {
          "selector": "#s5",
          "note": "Checked by default. Consent to marketing."
        },
        {
          "selector": "#s6",
          "note": "Checked by default. Consent to sharing data with partners."
        }
      ],
      "expectedFindings": 4,
      "fairTwin": "The same boxes, empty. Near-misses on both versions: an unchecked add-on (#s2) and a harmless checked default (#s4)."
    },
    {
      "id": "confirmshaming",
      "url": "/lab/confirmshaming",
      "pattern": "confirmshaming",
      "title": "Declines that shame",
      "targets": [
        {
          "selector": "#s1",
          "note": "A decline button inside a sign-up box."
        },
        {
          "selector": "#s2",
          "note": "A decline link inside a banner."
        },
        {
          "selector": "#s4",
          "note": "A decline button on an exit offer."
        }
      ],
      "expectedFindings": 3,
      "fairTwin": "Every decline says “No thanks”, the same size and style as the yes. Near-miss: a plain “Not now” (#s3)."
    },
    {
      "id": "countdown-reset",
      "url": "/lab/countdown-reset",
      "pattern": "fake-countdown",
      "title": "A countdown that restarts on reload",
      "targets": [
        {
          "selector": "#s1",
          "note": "Starts at 09:59 on every page load. One visit cannot prove it: load the page, wait, and load it again."
        }
      ],
      "expectedFindings": 1,
      "fairTwin": "No countdown. The price is the price."
    },
    {
      "id": "countdown-loop",
      "url": "/lab/countdown-loop",
      "pattern": "fake-countdown",
      "title": "A countdown that starts over",
      "targets": [
        {
          "selector": "#s1",
          "note": "Starts at 00:20, reaches 00:00 and silently starts again. Provable in one visit of about 25 seconds."
        }
      ],
      "expectedFindings": 1,
      "fairTwin": "No countdown. The price does not depend on how long you take."
    },
    {
      "id": "scarcity",
      "url": "/lab/scarcity",
      "pattern": "fake-scarcity",
      "title": "Stock and crowd claims",
      "targets": [
        {
          "selector": "#s1",
          "note": "A stock number re-drawn every 2 to 3 seconds. It goes up as well as down."
        },
        {
          "selector": "#s2",
          "note": "A viewer count re-drawn every 2 to 3 seconds (pattern: fake-activity)."
        },
        {
          "selector": "#s3",
          "note": "A demand claim with nothing behind it."
        }
      ],
      "expectedFindings": 3,
      "fairTwin": "“In stock”, and nothing about other shoppers. Near-miss on both versions: “30-day returns” (#s4), a number that is a plain fact."
    },
    {
      "id": "buried-terms",
      "url": "/lab/buried-terms",
      "pattern": "buried-terms",
      "title": "Renewal terms in small or pale text",
      "targets": [
        {
          "selector": "#s1",
          "note": "Renewal terms at 9 pixels. The contrast is fine; the size is the problem."
        },
        {
          "selector": "#s2",
          "note": "Billing terms at 15 pixels in pale grey, about 2 to 1 against white."
        }
      ],
      "expectedFindings": 2,
      "fairTwin": "The same terms at 17 pixels and full contrast, above the button."
    },
    {
      "id": "consent-asymmetric",
      "url": "/lab/consent-asymmetric",
      "pattern": "lopsided-consent",
      "title": "Consent with a lopsided choice",
      "targets": [
        {
          "selector": "#s1",
          "note": "The reject control: an 11-pixel pale link under a full-width accept button (#s2)."
        }
      ],
      "expectedFindings": 1,
      "fairTwin": "Reject all and Accept all as two buttons of the same size and style."
    },
    {
      "id": "consent-no-reject",
      "url": "/lab/consent-no-reject",
      "pattern": "no-reject",
      "title": "Consent with no way to refuse",
      "targets": [
        {
          "selector": "#s1",
          "note": "The dialog: Accept all or Manage options. Reject all exists only on the second screen."
        }
      ],
      "expectedFindings": 1,
      "fairTwin": "Reject all, Accept all and Choose on the first screen, as equal buttons."
    }
  ],
  "controls": [
    {
      "id": "control-shop",
      "url": "/lab/control-shop",
      "title": "An honest product page",
      "expectedFindings": 0,
      "why": "An empty newsletter box, a harmless checked default, a plain “No thanks”, “In stock”, renewal terms at 17 pixels and full contrast, and opening hours that look like a timer and never tick."
    },
    {
      "id": "control-consent",
      "url": "/lab/control-consent",
      "title": "An honest consent dialog",
      "expectedFindings": 0,
      "why": "Reject all and Accept all are the same size and style, on the first screen."
    },
    {
      "id": "control-deadline",
      "url": "/lab/control-deadline",
      "title": "A real deadline",
      "expectedFindings": 0,
      "why": "The countdown runs to midnight UTC. Reload and it has moved on by exactly the time that passed."
    }
  ]
}
