code-factory-4-design 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: code-factory-4-design
3
+ Version: 0.3.0
4
+ Summary: The Digital Architect of Prestige โ€” a coding-agent skill for designing premium, high-conversion websites and mobile UIs using applied psychology and trust engineering. Ships a SKILL.md contract plus an executable design-linter that audits generated HTML/CSS against the Halo Effect, Cognitive Fluency, Trust Engineering, Peak-End Rule, and Horn-Effect triggers.
5
+ License-Expression: MIT OR Apache-2.0
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE-APACHE
9
+ License-File: LICENSE-MIT
10
+ License-File: NOTICE
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+ Dynamic: license-file
14
+
15
+ # The Digital Architect of Prestige ๐Ÿ’Ž
16
+
17
+ A **coding-agent skill** for designing premium, high-conversion websites and
18
+ mobile UIs โ€” for Claude Code, Codex, OpenCode, Cursor, and any agent that reads
19
+ a project skill file. It moves the agent beyond aesthetics into **applied
20
+ psychology and trust engineering**, and ships an **executable design linter** so
21
+ the agent can *verify* its output against the principles, not just intend them.
22
+
23
+ > Users judge credibility in ~50ms from visuals alone. If it looks
24
+ > professional, they assume the service is too (the Halo Effect). This skill
25
+ > engineers that halo โ€” and refuses to let a pretty page ship over a broken one.
26
+
27
+ ## Workflow at a glance
28
+
29
+ ```mermaid
30
+ flowchart LR
31
+ A["User journey or page brief"] --> B["Select workflow and purpose"]
32
+ B --> C["Build premium UI"]
33
+ C --> D["Audit five laws"]
34
+ D --> E["Score bias, CTA, hook, mobile, and purpose fit"]
35
+ E -->|"fails"| F["Fix ranked recommendations"]
36
+ F --> D
37
+ E -->|"passes"| G["Ship UI with design receipt"]
38
+ ```
39
+
40
+ ## Install into your agent (any OS)
41
+
42
+ ```bash
43
+ python install.py # Windows / macOS / Linux (installs + verifies)
44
+ # then, inside your project:
45
+ prestige install claude # or: codex ยท opencode ยท cursor ยท generic
46
+ ```
47
+ That drops `SKILL.md` where your agent auto-reads it. From then on, any
48
+ "design a landing page / make this premium / build a checkout" request triggers
49
+ the skill.
50
+
51
+ ## What the agent gets
52
+
53
+ **A design contract (`SKILL.md`)** encoding five laws it applies every time:
54
+
55
+ 1. **The 50ms Halo** โ€” hero section, one bold headline, high-fidelity cover imagery.
56
+ 2. **Cognitive Fluency** โ€” Hick's/Miller's Law, extreme whitespace, one goal per section, `line-height โ‰ฅ 1.6`.
57
+ 3. **Trust Engineering** โ€” security + social-proof cues at decision points, total price incl. fees upfront, guest checkout.
58
+ 4. **Peak-End Rule** โ€” micro-interactions at the peaks, inline form validation, a rewarding confirmation ending.
59
+ 5. **Horn-Effect Defense** โ€” one Visual DNA in CSS variables, mobile-first stability, no dated cues.
60
+
61
+ **An executable linter (`prestige audit`)** that scores generated HTML/CSS on all
62
+ five laws and **hard-fails on major functional flaws** โ€” because the skill's one
63
+ non-negotiable rule is *never let aesthetics mask a broken primary action or an
64
+ inaccessible UI.*
65
+
66
+ ```bash
67
+ prestige scaffold site.html # premium starter (scores 100/100)
68
+ prestige audit site.html # score the five laws + catch Horn-Effect triggers
69
+ prestige purpose site.html --purpose developer # opinionated purpose-fit audit
70
+ prestige audit site.html --strict # exit 1 if failing โ€” use as an agent/CI gate
71
+ ```
72
+
73
+ Example on a deliberately clunky page:
74
+ ```
75
+ Overall: 18/100 Verdict: NEEDS WORK
76
+ halo:15 fluency:20 trust:20 peak:20 horn:15
77
+ โ›” HARD FAILURES (fix before shipping โ€” function over aesthetics):
78
+ MAJOR_FLAW: primary action button has no accessible label/text.
79
+ โœ— [trust] T_NO_SECURITY: Transactional UI with no security cue near actions.
80
+ โœ— [horn] C_NO_VIEWPORT: Missing viewport meta โ€” mobile layout will break.
81
+ ```
82
+
83
+ ## The workflow the skill teaches
84
+
85
+ `Discovery` (audit the journey for trust gaps) โ†’ `Build` (apply the five laws)
86
+ โ†’ `Verify` (`prestige audit`, fix flags, then present). The agent treats a
87
+ failing audit like a failing test โ€” which is exactly how "premium" stops being
88
+ a vibe and becomes a checkable property.
89
+
90
+ ## The psychology, sourced
91
+ Halo Effect & the 50ms window ยท Aesthetic-Usability Effect ยท Hick's & Miller's
92
+ Law ยท Fitts's Law ยท Peak-End Rule ยท Negativity/Horn Effect. Detailed playbooks
93
+ in `references/` (hero-halo, cognitive-fluency, trust-engineering, peak-end).
94
+
95
+ ## v0.2 โ€” deeper psychology, stricter scoring, five workflows
96
+
97
+ - **Cognitive Bias Engine** โ€” six research-backed biases (Von Restorff, Anchoring,
98
+ Social Proof, Loss Aversion, Reciprocity, Cognitive Fluency) with detectors,
99
+ quality scoring, and precise recommendations.
100
+ - **CTA & Hook Verification** โ€” scores whether your call-to-action and headline
101
+ are engineered to convert (weak-verb detection, reader-focus, power words,
102
+ CTA-competition penalty) with exact rewrites.
103
+ - **Strict Mobile Judge** โ€” thumb-reach tap targets, viewport discipline, real
104
+ breakpoints, legible base text, sticky-CTA guidance โ€” because trust breaks on
105
+ mobile first.
106
+ - **Five Design-Principle Workflows** โ€” Conversion Architect, Luxury Minimalist,
107
+ Trust Engineer, Editorial Storyteller, Product-Led Pragmatist. Each is a
108
+ distinct optimization strategy that re-weights the score.
109
+ - **Precision Scoring** โ€” `prestige score <file> --workflow <key>` fuses laws +
110
+ biases + CTA/hooks + mobile into a 0โ€“100 conversion-readiness grade with
111
+ **recommendations ranked by conversion impact**.
112
+
113
+ ```bash
114
+ prestige workflows # see the five lenses
115
+ prestige score site.html --workflow trust # precise score + ranked fixes
116
+ prestige score site.html --strict # exit 1 if not conversion-ready
117
+ ```
118
+
119
+ Grounded in current research: credibility forms in ~0.05s (Google), trigger-driven
120
+ UX lifts conversion ~34% on average (Nielsen Norman 2024).
121
+
122
+ ## v0.3 - purpose-fit design judgment
123
+
124
+ Workflows answer **what the page optimizes for**. Purpose lenses answer **what
125
+ psychological job the design must do for this audience and domain**.
126
+
127
+ Prestige now ships deterministic purpose-fit profiles:
128
+
129
+ - `developer` - concrete proof, docs, CLI/API clarity, GitHub/demo trust.
130
+ - `healthcare` - calm reassurance, privacy, clinician proof, no miracle hype.
131
+ - `fintech` - security, transparent fees/rates, control, compliance cues.
132
+ - `luxury` - restraint, craft, whitespace, fewer louder elements.
133
+ - `marketplace` - bilateral buyer/seller trust, reviews, protection, comparison.
134
+ - `saas` - product visibility, integrations, ROI, low-friction activation.
135
+ - `editorial` - narrative rhythm, sources, evidence, satisfying end beat.
136
+
137
+ ```bash
138
+ prestige purposes
139
+ prestige purpose site.html --purpose healthcare --strict
140
+ prestige score site.html --workflow trust --purpose healthcare
141
+ ```
142
+
143
+ The purpose gate scores intent clarity, proof fit, visual theme fit, action
144
+ language, and purpose-specific anti-patterns. This makes Prestige more
145
+ opinionated in the right way: not just "pretty," but fit for the decision the
146
+ interface exists to change.
147
+
148
+ ## License
149
+ Dual-licensed **Apache-2.0 OR MIT** โ€” pick whichever your project prefers.
150
+ ## Criterion attribution
151
+
152
+ Prestige 0.2.1 wraps its existing five per-law scores in the factory attribution
153
+ envelope. Run `prestige audit page.html --json` to receive one unit per design
154
+ criterion, its score and threshold, and the unchanged overall verdict.
@@ -0,0 +1,19 @@
1
+ code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-APACHE,sha256=7E7Abez5cUuQccTXlM_Tcx4MlEraqeuGX67rlx1dOyQ,487
2
+ code_factory_4_design-0.3.0.dist-info/licenses/LICENSE-MIT,sha256=R_78kakzZsivM3qr_yf5z3A9FAz8vdBkt7J75-mrjJI,499
3
+ code_factory_4_design-0.3.0.dist-info/licenses/NOTICE,sha256=cj5FLrj-XYK-uwnkiggsRV62mzjmdCY6gkylrDscdoY,160
4
+ prestige_design/__init__.py,sha256=R1Th69fyPr-4k0hc9v2t1QxNgAz5lbGq1rfQ8xkgYK0,833
5
+ prestige_design/adapters.py,sha256=6lmJivNajewaFY0B4LAqvb6940G_l-PLwvSxIJ7Yj2E,1104
6
+ prestige_design/attribution.py,sha256=U4861YtbeHgj9iKNutAdYrRJ7xH4K95ET2InqzC1ntU,1471
7
+ prestige_design/audit.py,sha256=E435nynFautddK_Xf18_vOOqobS5IJaDhzl_M7ROm-U,8608
8
+ prestige_design/biases.py,sha256=NMQRGt4b293xn9Oc5gLaqg8iQzIoK0l4Ccc__yg5kE4,7592
9
+ prestige_design/cli.py,sha256=I6rI08R2Dm7ItR8EXkwxCj1VzomH2_PGTi5ELN6EUo0,11081
10
+ prestige_design/cta_hooks.py,sha256=jvfOPAE9DA2Rj0x17oeXwvYIG4Vg3nUrpDkrvB3-foI,4901
11
+ prestige_design/mobile_judge.py,sha256=pE8I771JDhN2b40CoWQZcwU3ze1gV-KgiDhLz-fVeFo,3100
12
+ prestige_design/purpose.py,sha256=sSje9XH-W6ZtZCJNRNVwlZfBoedVu-bqUylpc3zqjqw,13320
13
+ prestige_design/score.py,sha256=va8xFyKaYbQHncBznhE7iUotl2zBqLEqGomjTe3szWg,5284
14
+ prestige_design/workflows.py,sha256=v3yQNrYlJYgvKXBUF_24-S_HxyLNzRG4ozQBHP4bsMU,5657
15
+ code_factory_4_design-0.3.0.dist-info/METADATA,sha256=X4hQyLMeXQ6mHzi-wD6gbuls5jCbHd1DrQNlORRdpAQ,7540
16
+ code_factory_4_design-0.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
+ code_factory_4_design-0.3.0.dist-info/entry_points.txt,sha256=WX3IpwEGRfVagRGz_CWcwOcEngfm4-mO_dOoj63AbSA,54
18
+ code_factory_4_design-0.3.0.dist-info/top_level.txt,sha256=wSDpQ6UwJGuiscsml8WKqaWUchughfGx53IxsFGQQP4,16
19
+ code_factory_4_design-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ prestige = prestige_design.cli:main
@@ -0,0 +1,8 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy at http://www.apache.org/licenses/LICENSE-2.0
6
+ Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
7
+ KIND. Full text: http://www.apache.org/licenses/LICENSE-2.0.txt
8
+ Copyright 2026 WizeMe.APP
@@ -0,0 +1,10 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WizeMe.APP
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including the rights to use, copy, modify,
8
+ merge, publish, distribute, sublicense, and/or sell copies, subject to
9
+ including the above notice. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
10
+ OF ANY KIND. Full text: https://opensource.org/licenses/MIT
@@ -0,0 +1,3 @@
1
+ The Digital Architect of Prestige โ€” premium design skill for coding agents
2
+ Copyright 2026 WizeMe.APP
3
+ Dual-licensed under Apache-2.0 OR MIT at your option.
@@ -0,0 +1 @@
1
+ prestige_design
@@ -0,0 +1,15 @@
1
+ """The Digital Architect of Prestige โ€” premium web/mobile design as a skill.
2
+
3
+ Encodes five design laws as an executable audit so any coding agent (Claude
4
+ Code / Codex / OpenCode / Cursor / โ€ฆ) doesn't just *know* the principles but
5
+ can *verify* its own output before shipping:
6
+
7
+ 1. The 50ms Halo โ€” hero section, high-fidelity imagery, instant credibility
8
+ 2. Cognitive Fluency โ€” Hick's/Miller's Law, whitespace, one goal per section
9
+ 3. Trust Engineering โ€” security cues + social proof at decision points, price transparency
10
+ 4. Peak-End Rule โ€” micro-interactions at peaks, a rewarding conclusion
11
+ 5. Horn-Effect Defense โ€” ruthless consistency, mobile stability, no dated cues
12
+
13
+ The warning is enforced too: aesthetics never mask a MAJOR functional flaw.
14
+ """
15
+ __version__ = "0.3.0"
@@ -0,0 +1,28 @@
1
+ """Install this skill into any coding agent. The SKILL.md is the contract;
2
+ each agent reads it from a different location."""
3
+ from __future__ import annotations
4
+ from pathlib import Path
5
+
6
+ def skill_text() -> str:
7
+ return (Path(__file__).resolve().parent/"SKILL.md").read_text()
8
+
9
+ TARGETS = {
10
+ "claude": [".claude/skills/prestige-design/SKILL.md"],
11
+ "codex": ["AGENTS.md"], # appended, not overwritten
12
+ "opencode": [".opencode/skills/prestige-design.md"],
13
+ "cursor": [".cursor/rules/prestige-design.md"],
14
+ "generic": ["SKILL-prestige-design.md"],
15
+ }
16
+
17
+ def install(root: Path, agent: str) -> list[Path]:
18
+ root = Path(root); created = []
19
+ key = {"claude-code":"claude"}.get(agent, agent)
20
+ text = skill_text()
21
+ for rel in TARGETS.get(key, ["AGENTS.md"]):
22
+ dst = root/rel; dst.parent.mkdir(parents=True, exist_ok=True)
23
+ if dst.name == "AGENTS.md" and dst.exists():
24
+ dst.write_text(dst.read_text().rstrip()+"\n\n"+text)
25
+ else:
26
+ dst.write_text(text)
27
+ created.append(dst)
28
+ return created
@@ -0,0 +1,51 @@
1
+ """Attribution wrapper for Prestige's existing per-criterion scores."""
2
+ from dataclasses import asdict, dataclass
3
+ from enum import Enum
4
+
5
+
6
+ class FailureClass(str, Enum):
7
+ AMBIGUOUS_REQUIREMENT = "ambiguous_requirement"
8
+ UNTYPED_INPUT = "untyped_input"
9
+ SCOPE_ESCAPE = "scope_escape"
10
+ INVENTED_PARAM = "invented_param"
11
+ SIGNATURE_DRIFT = "signature_drift"
12
+ STUB_UNFILLED = "stub_unfilled"
13
+ COMPLEXITY_EXCEEDED = "complexity_exceeded"
14
+ INCONSISTENT_LOGIC = "inconsistent_logic"
15
+ RUNTIME_CRASH = "runtime_crash"
16
+ RUNTIME_TIMEOUT = "runtime_timeout"
17
+ WRONG_OUTPUT = "wrong_output"
18
+ ACCURACY_REGRESSION = "accuracy_regression"
19
+ NONDETERMINISM = "nondeterminism"
20
+ SECURITY_FINDING = "security_finding"
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class UnitResult:
25
+ unit: str
26
+ stage: str
27
+ passed: bool
28
+ evidence: str
29
+ failure_class: FailureClass | None = None
30
+
31
+
32
+ @dataclass
33
+ class Attribution:
34
+ stage: str
35
+ n_checked: int
36
+ n_passed: int
37
+ units: list[UnitResult]
38
+
39
+ @property
40
+ def rate(self):
41
+ return self.n_passed / self.n_checked if self.n_checked else 0.0
42
+
43
+ def dominant_failure_class(self):
44
+ return FailureClass.WRONG_OUTPUT if self.n_passed < self.n_checked else None
45
+
46
+ def to_dict(self):
47
+ value = asdict(self)
48
+ value["rate"] = self.rate
49
+ dominant = self.dominant_failure_class()
50
+ value["dominant_failure_class"] = dominant.value if dominant else None
51
+ return value
@@ -0,0 +1,148 @@
1
+ """The Prestige design linter โ€” encodes the five laws as executable checks so
2
+ an agent can VERIFY its output, not just intend it. Pure-stdlib HTML/CSS
3
+ heuristics; no browser needed. Returns per-law scores + Horn-Effect triggers +
4
+ hard failures for major functional flaws (the non-negotiable warning)."""
5
+ from __future__ import annotations
6
+ import re
7
+ from dataclasses import dataclass, field
8
+
9
+ @dataclass
10
+ class Finding:
11
+ law: str
12
+ level: str # "pass" | "warn" | "fail" | "hard_fail"
13
+ code: str
14
+ message: str
15
+
16
+ @dataclass
17
+ class AuditReport:
18
+ findings: list = field(default_factory=list)
19
+ scores: dict = field(default_factory=dict) # law -> 0..100
20
+ hard_failures: list = field(default_factory=list)
21
+
22
+ @property
23
+ def passed(self) -> bool:
24
+ return not self.hard_failures and all(v >= 60 for v in self.scores.values())
25
+
26
+ @property
27
+ def overall(self) -> int:
28
+ return round(sum(self.scores.values()) / len(self.scores)) if self.scores else 0
29
+
30
+ @property
31
+ def attribution(self):
32
+ from .attribution import Attribution, FailureClass, UnitResult
33
+ units = [
34
+ UnitResult(
35
+ unit=f"criterion:{law}",
36
+ stage="design_audit",
37
+ passed=score >= 60,
38
+ evidence=f"score={score}; threshold=60",
39
+ failure_class=None if score >= 60 else FailureClass.WRONG_OUTPUT,
40
+ )
41
+ for law, score in sorted(self.scores.items())
42
+ ]
43
+ return Attribution("design_audit", len(units), sum(unit.passed for unit in units), units)
44
+
45
+ def _has(pattern, text, flags=re.I):
46
+ return re.search(pattern, text, flags) is not None
47
+
48
+ def _count(pattern, text, flags=re.I):
49
+ return len(re.findall(pattern, text, flags))
50
+
51
+ def audit_html(html: str, css: str = "") -> AuditReport:
52
+ """css may be inline in html; we scan both together for style checks."""
53
+ blob = html + "\n" + css
54
+ style = css + "\n" + " ".join(re.findall(r"<style[^>]*>(.*?)</style>", html, re.S | re.I))
55
+ r = AuditReport()
56
+ f = r.findings
57
+
58
+ # ---------- LAW 1: 50ms Halo (hero) ----------
59
+ s = 100
60
+ has_hero = _has(r'class=["\'][^"\']*hero', html) or _has(r'<header', html) or _has(r'<section[^>]*hero', html)
61
+ if not has_hero:
62
+ s -= 40; f.append(Finding("halo","warn","H_NO_HERO","No identifiable hero/header section for the 50ms credibility window."))
63
+ if not _has(r'object-fit\s*:\s*cover', style) and not _has(r'background-size\s*:\s*cover', style):
64
+ s -= 20; f.append(Finding("halo","warn","H_NO_COVER_IMG","No object-fit/background cover image โ€” hero imagery drives the halo."))
65
+ h1 = _count(r'<h1', html)
66
+ if h1 == 0:
67
+ s -= 25; f.append(Finding("halo","fail","H_NO_H1","No <h1> โ€” the hero needs one bold headline."))
68
+ elif h1 > 1:
69
+ s -= 15; f.append(Finding("halo","warn","H_MULTI_H1",f"{h1} <h1> tags โ€” a single bold headline signals confidence."))
70
+ r.scores["halo"] = max(s, 0)
71
+
72
+ # ---------- LAW 2: Cognitive Fluency ----------
73
+ s = 100
74
+ navlinks = _count(r'<nav[^>]*>.*?</nav>', html, re.S)
75
+ nav_items = 0
76
+ for nav in re.findall(r'<nav[^>]*>(.*?)</nav>', html, re.S | re.I):
77
+ nav_items = max(nav_items, _count(r'<a\b', nav))
78
+ if nav_items > 7:
79
+ s -= 25; f.append(Finding("fluency","warn","F_NAV_OVERLOAD",f"Primary nav has {nav_items} items (Hick's Law: keep โ‰ค7)."))
80
+ if not _has(r'line-height\s*:\s*(1\.[6-9]|[2-9])', style):
81
+ s -= 20; f.append(Finding("fluency","warn","F_TIGHT_LEADING","Body line-height < 1.6 hurts readability/fluency."))
82
+ # whitespace: look for generous section padding
83
+ pads = re.findall(r'padding[^;:]*:\s*([0-9.]+)(rem|vw|vh|em|px)', style, re.I)
84
+ generous = any((u in ("rem","vw","em") and float(v) >= 3) or (u=="px" and float(v)>=48) for v,u in pads)
85
+ if not generous:
86
+ s -= 20; f.append(Finding("fluency","warn","F_NO_WHITESPACE","No generous section padding โ€” extreme whitespace signals premium."))
87
+ if not _has(r'display\s*:\s*(grid|flex)', style):
88
+ s -= 15; f.append(Finding("fluency","warn","F_NO_CHUNKING","No grid/flex layout โ€” use it to chunk info (Miller's Law)."))
89
+ r.scores["fluency"] = max(s, 0)
90
+
91
+ # ---------- LAW 3: Trust Engineering ----------
92
+ s = 100
93
+ trust_cues = _count(r'(secure|verified|guarantee|padlock|lock-icon|badge|ssl|encrypt|trusted)', blob)
94
+ social = _has(r'(review|rating|stars?|testimonial|โ˜…|โญ|\bfrom\s+\d+\s+reviews?)', blob)
95
+ is_transactional = _has(r'(checkout|buy|purchase|book now|subscribe|sign\s*up|pay\b|add to cart|price|\$\d)', blob)
96
+ if is_transactional:
97
+ if trust_cues == 0:
98
+ s -= 35; f.append(Finding("trust","fail","T_NO_SECURITY","Transactional UI with no security/verification cue near actions."))
99
+ if not social:
100
+ s -= 25; f.append(Finding("trust","warn","T_NO_SOCIAL_PROOF","No social proof (reviews/ratings) near decision points."))
101
+ # price transparency: look for fee/tax mention if a price is shown
102
+ if _has(r'\$\d', blob) and not _has(r'(total|incl\.|including|fees?|tax|all-in)', blob):
103
+ s -= 20; f.append(Finding("trust","warn","T_HIDDEN_COST","Price shown without total/fees/tax transparency (shock-factor risk)."))
104
+ if _has(r'(create account|register).{0,40}(required|to continue)', blob) and not _has(r'guest', blob):
105
+ s -= 15; f.append(Finding("trust","warn","T_FORCED_ACCOUNT","Forced account with no guest option (~26% abandonment)."))
106
+ else:
107
+ # non-transactional: trust cues optional but rewarded
108
+ if trust_cues == 0 and not social:
109
+ s -= 10; f.append(Finding("trust","warn","T_LIGHT_TRUST","Consider adding credibility cues (logos, testimonials)."))
110
+ r.scores["trust"] = max(s, 0)
111
+
112
+ # ---------- LAW 4: Peak-End Rule ----------
113
+ s = 100
114
+ if not _has(r'transition\s*:', style):
115
+ s -= 30; f.append(Finding("peak","warn","P_NO_TRANSITIONS","No CSS transitions โ€” micro-interactions create peaks of delight."))
116
+ if not _has(r':hover', style):
117
+ s -= 25; f.append(Finding("peak","warn","P_NO_HOVER","No :hover states โ€” buttons should feel satisfying to interact with."))
118
+ has_form = _has(r'<form|<input', html)
119
+ if has_form and not (_has(r':valid|:invalid', style) or _has(r'(checkmark|โœ“|valid)', blob)):
120
+ s -= 25; f.append(Finding("peak","warn","P_NO_INLINE_VALID","Form without inline validation feedback (gentle confirmation)."))
121
+ r.scores["peak"] = max(s, 0)
122
+
123
+ # ---------- LAW 5: Horn-Effect Defense (consistency + mobile) ----------
124
+ s = 100
125
+ if not _has(r'@media', style):
126
+ s -= 40; f.append(Finding("horn","fail","C_NO_RESPONSIVE","No @media query โ€” broken mobile is an instant unreliability signal."))
127
+ if not _has(r'var\(--|:root', style):
128
+ s -= 25; f.append(Finding("horn","warn","C_NO_CSS_VARS","No CSS variables โ€” hard to keep Visual DNA consistent."))
129
+ if not _has(r'<meta[^>]*viewport', html):
130
+ s -= 20; f.append(Finding("horn","fail","C_NO_VIEWPORT","Missing viewport meta โ€” mobile layout will break."))
131
+ font_families = set(re.findall(r'font-family\s*:\s*([^;]+)', style, re.I))
132
+ if len(font_families) > 3:
133
+ s -= 15; f.append(Finding("horn","warn","C_FONT_SOUP",f"{len(font_families)} distinct font-family declarations โ€” inconsistency risks the Horn Effect."))
134
+ r.scores["horn"] = max(s, 0)
135
+
136
+ # ---------- HARD FAILS: major functional flaws (the non-negotiable warning) ----------
137
+ # a pretty page must never ship over a broken PRIMARY action / accessibility
138
+ buttons = re.findall(r'<(button|a)\b[^>]*>(.*?)</\1>', html, re.S | re.I)
139
+ unlabeled = [b for tag,b in buttons if not re.sub(r'<[^>]+>','',b).strip() and 'aria-label' not in b]
140
+ if is_transactional and unlabeled:
141
+ r.hard_failures.append("MAJOR_FLAW: primary action button has no accessible label/text.")
142
+ f.append(Finding("warning","hard_fail","X_UNLABELED_CTA","A primary action is unlabeled โ€” major usability flaw; fix before aesthetics."))
143
+ imgs = re.findall(r'<img\b[^>]*>', html, re.I)
144
+ missing_alt = [i for i in imgs if 'alt=' not in i]
145
+ if len(missing_alt) >= max(3, len(imgs)//2) and imgs:
146
+ r.hard_failures.append(f"MAJOR_FLAW: {len(missing_alt)}/{len(imgs)} images missing alt text (accessibility).")
147
+ f.append(Finding("warning","hard_fail","X_NO_ALT","Widespread missing alt text โ€” accessibility failure blocks ship."))
148
+ return r
@@ -0,0 +1,119 @@
1
+ """The Cognitive Bias Engine โ€” maps psychological triggers to concrete,
2
+ detectable interface elements. This is the depth upgrade: instead of five
3
+ vague 'laws', the audit checks whether specific, research-backed biases are
4
+ actually engaged in the markup, and scores how well each is executed.
5
+
6
+ Credibility judgments form in ~0.05s (Google Research); trigger-driven UX
7
+ lifts conversion ~34% on average (Nielsen Norman 2024). Each bias below has:
8
+ a detector (is it present?), a quality check (is it done right?), and a
9
+ precise recommendation (how to fix it).
10
+ """
11
+ from __future__ import annotations
12
+ import re
13
+ from dataclasses import dataclass, field
14
+
15
+ @dataclass
16
+ class BiasCheck:
17
+ name: str
18
+ present: bool
19
+ quality: float # 0..1 โ€” how well executed when present
20
+ weight: int # conversion impact weight
21
+ recommendation: str = ""
22
+ evidence: str = ""
23
+
24
+ def _has(p, t): return re.search(p, t, re.I) is not None
25
+ def _count(p, t): return len(re.findall(p, t, re.I))
26
+
27
+ def check_von_restorff(html: str, css: str) -> BiasCheck:
28
+ """Isolation effect: the CTA must visually stand out. Single, contrasting
29
+ primary button. Competing equal-weight CTAs KILL this (a documented mistake)."""
30
+ buttons = re.findall(r'<(?:button|a)[^>]*class=["\'][^"\']*(?:btn|button|cta)[^"\']*["\'][^>]*>', html, re.I)
31
+ primary_markers = _count(r'(btn-primary|cta-primary|primary|hero-cta)', html)
32
+ has_contrast = _has(r'(background|bg-)[^;]*(var\(--|#[0-9a-f]{3,6}|rgb)', css)
33
+ present = len(buttons) > 0 or _count(r'<button', html) > 0
34
+ # too many equal CTAs = failure of isolation
35
+ total_ctas = _count(r'<button|class=["\'][^"\']*(btn|cta)', html)
36
+ quality = 1.0
37
+ rec = ""
38
+ if total_ctas > 4:
39
+ quality = 0.4; rec = f"{total_ctas} button-like elements compete โ€” isolate ONE primary CTA with unique color/size (Von Restorff)."
40
+ elif primary_markers == 0 and total_ctas > 1:
41
+ quality = 0.6; rec = "No distinct 'primary' CTA class โ€” mark the single most important action so it stands out."
42
+ elif not has_contrast:
43
+ quality = 0.7; rec = "Primary CTA needs a contrasting color vs background to trigger the isolation effect."
44
+ return BiasCheck("Von Restorff (CTA isolation)", present, quality, 20, rec,
45
+ f"{total_ctas} CTA-like elements, {primary_markers} marked primary")
46
+
47
+ def check_anchoring(html: str, css: str) -> BiasCheck:
48
+ """Anchoring: show a higher reference price next to the actual price so the
49
+ real price reads as a deal. Present only relevant on pricing UIs."""
50
+ has_price = _has(r'[\$ยฃโ‚ฌ]\s?\d', html)
51
+ if not has_price:
52
+ return BiasCheck("Anchoring (price)", False, 1.0, 12, "", "no pricing shown (N/A)")
53
+ # look for a struck-through / original / 'was' price near the active one
54
+ has_anchor = (_has(r'(was|originally|reg\.|<s>|<del>|line-through|strikethrough|compare)', html) or
55
+ _has(r'text-decoration\s*:\s*line-through', css))
56
+ quality = 1.0 if has_anchor else 0.3
57
+ rec = "" if has_anchor else "Pricing shown with no anchor โ€” display an original/higher reference price (e.g. 'Was $199, now $99') so the real price reads as a deal."
58
+ return BiasCheck("Anchoring (price)", True, quality, 12, rec,
59
+ "anchor present" if has_anchor else "no reference anchor")
60
+
61
+ def check_loss_aversion(html: str) -> BiasCheck:
62
+ """Scarcity / urgency / FOMO: limited stock, countdowns, 'only N left'.
63
+ Powerful but must be genuine โ€” flag but reward when present."""
64
+ is_transactional = _has(r'(buy|checkout|purchase|book|subscribe|order|add to cart)', html)
65
+ if not is_transactional:
66
+ return BiasCheck("Loss Aversion (scarcity/urgency)", False, 1.0, 10, "", "not transactional (N/A)")
67
+ has_scarcity = _has(r'(only \d+[\w\s]{0,15}left|limited|selling fast|\d+ (seats?|left|spots?|in stock)|ends? (in|soon)|today only|last chance|launch pricing|hurry|\d+ people (are )?viewing)', html)
68
+ has_urgency = _has(r'(countdown|timer|expires?|deadline|\d+:\d+:\d+)', html)
69
+ present = has_scarcity or has_urgency
70
+ quality = 1.0 if present else 0.5
71
+ rec = "" if present else "Transactional page with no scarcity/urgency cue โ€” a genuine 'only N left' or time-limited signal engages loss aversion (use only if TRUE)."
72
+ return BiasCheck("Loss Aversion (scarcity/urgency)", present, quality, 10, rec,
73
+ "scarcity/urgency present" if present else "none")
74
+
75
+ def check_social_proof(html: str) -> BiasCheck:
76
+ """Social proof near the decision point. Best: specific numbers + near CTA."""
77
+ has_proof = _has(r'(review|rating|testimonial|โ˜…|โญ|\d+[\d,]*\+? (customers|users|happy|reviews)|trusted by|as seen (in|on)|join \d)', html)
78
+ has_specific = _has(r'\d[\d,]*\+?\s*(customers|users|reviews|companies|teams|happy)', html)
79
+ has_logos = _has(r'(logo|featured|as-seen|press|forbes|techcrunch|trusted-by)', html)
80
+ present = has_proof
81
+ quality = 1.0 if (has_specific and (has_proof or has_logos)) else 0.6 if present else 0.0
82
+ rec = ("" if quality >= 1.0 else
83
+ "Add specific social proof numbers ('Join 5,000+ customers') and trust logos near the CTA." if not has_specific else
84
+ "Social proof present โ€” move it adjacent to the primary CTA for maximum effect.")
85
+ return BiasCheck("Social Proof", present, quality, 18, rec,
86
+ "specific + logos" if (has_specific and has_logos) else "present" if present else "absent")
87
+
88
+ def check_cognitive_fluency(html: str, css: str) -> BiasCheck:
89
+ """Easy-to-process = trustworthy. Whitespace, hierarchy, short blocks."""
90
+ has_whitespace = bool(re.search(r'padding[^;:]*:\s*([3-9]|\d{2,})(rem|vw|vh|em)', css, re.I))
91
+ has_hierarchy = _count(r'<h[12]', html) >= 1 and _has(r'font-size', css)
92
+ has_generous_leading = _has(r'line-height\s*:\s*(1\.[6-9]|[2-9])', css)
93
+ score = sum([has_whitespace, has_hierarchy, has_generous_leading]) / 3
94
+ rec = ""
95
+ if not has_whitespace: rec = "Increase section padding (โ‰ฅ6vw) โ€” whitespace reads as premium and reduces cognitive load."
96
+ elif not has_generous_leading: rec = "Set body line-height โ‰ฅ1.6 โ€” easier processing reads as more trustworthy (fluency effect)."
97
+ return BiasCheck("Cognitive Fluency", True, score, 15, rec,
98
+ f"whitespace={has_whitespace}, hierarchy={has_hierarchy}, leading={has_generous_leading}")
99
+
100
+ def check_reciprocity(html: str) -> BiasCheck:
101
+ """Give before you ask: free trial, free guide, value upfront."""
102
+ has_give = _has(r'(free|trial|no (credit card|cost)|complimentary|gift|download.{0,20}free|get started free)', html)
103
+ is_transactional = _has(r'(buy|subscribe|sign\s*up|checkout|book)', html)
104
+ if not is_transactional:
105
+ return BiasCheck("Reciprocity", has_give, 1.0 if has_give else 0.7, 8, "", "N/A")
106
+ quality = 1.0 if has_give else 0.5
107
+ rec = "" if has_give else "Offer value before asking โ€” a free trial/guide/'no credit card' cue triggers reciprocity and lowers signup friction."
108
+ return BiasCheck("Reciprocity", has_give, quality, 8, rec,
109
+ "value-first present" if has_give else "asks without giving")
110
+
111
+ def run_bias_suite(html: str, css: str) -> list[BiasCheck]:
112
+ return [
113
+ check_von_restorff(html, css),
114
+ check_social_proof(html),
115
+ check_cognitive_fluency(html, css),
116
+ check_anchoring(html, css),
117
+ check_loss_aversion(html),
118
+ check_reciprocity(html),
119
+ ]