fraim-framework 2.0.166 → 2.0.168
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.
- package/dist/src/ai-hub/catalog.js +43 -36
- package/dist/src/ai-hub/server.js +28 -5
- package/dist/src/cli/commands/init-project.js +1 -98
- package/dist/src/cli/commands/manager.js +40 -0
- package/dist/src/cli/commands/sync.js +17 -21
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/utils/github-workflow-sync.js +12 -146
- package/dist/src/cli/utils/manager-pack-sync.js +188 -0
- package/dist/src/cli/utils/manager-publish.js +76 -0
- package/dist/src/cli/utils/user-config.js +20 -0
- package/dist/src/core/config-loader.js +9 -5
- package/dist/src/core/fraim-config-schema.generated.js +85 -31
- package/dist/src/core/manager-pack.js +26 -0
- package/dist/src/core/utils/local-registry-resolver.js +8 -1
- package/dist/src/first-run/install-state.js +1 -0
- package/dist/src/first-run/server.js +9 -0
- package/dist/src/first-run/session-service.js +117 -23
- package/dist/src/first-run/types.js +2 -5
- package/dist/src/local-mcp-server/learning-context-builder.js +45 -8
- package/dist/src/local-mcp-server/stdio-server.js +28 -0
- package/index.js +1 -1
- package/package.json +4 -1
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
- package/public/ai-hub/review.css +13 -0
- package/public/ai-hub/script.js +199 -5
- package/public/ai-hub/styles.css +28 -0
- package/public/first-run/index.html +1 -1
- package/public/first-run/script.js +459 -530
- package/public/first-run/styles.css +288 -73
- package/public/portfolio/ashley.html +523 -0
- package/public/portfolio/auditya.html +83 -0
- package/public/portfolio/banke.html +83 -0
- package/public/portfolio/beza.html +659 -0
- package/public/portfolio/careena.html +632 -0
- package/public/portfolio/casey.html +568 -0
- package/public/portfolio/celia.html +490 -0
- package/public/portfolio/deidre.html +642 -0
- package/public/portfolio/gautam.html +597 -0
- package/public/portfolio/hari.html +469 -0
- package/public/portfolio/huxley.html +1354 -0
- package/public/portfolio/index.html +741 -0
- package/public/portfolio/maestro.html +518 -0
- package/public/portfolio/mandy.html +590 -0
- package/public/portfolio/mona.html +597 -0
- package/public/portfolio/pam.html +887 -0
- package/public/portfolio/procella.html +107 -0
- package/public/portfolio/qasm.html +569 -0
- package/public/portfolio/ricardo.html +489 -0
- package/public/portfolio/sade.html +560 -0
- package/public/portfolio/sam.html +654 -0
- package/public/portfolio/sechar.html +580 -0
- package/public/portfolio/sreya.html +599 -0
- package/public/portfolio/swen.html +601 -0
- package/dist/src/ai-hub/word-sideload.js +0 -95
- package/dist/src/cli/commands/test-mcp.js +0 -171
- package/dist/src/cli/setup/first-run.js +0 -242
- package/dist/src/core/config-writer.js +0 -75
- package/dist/src/core/utils/job-aliases.js +0 -47
- package/dist/src/core/utils/workflow-parser.js +0 -174
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="light">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>DEIdre · AI Inclusion Leader · FRAIM Portfolio</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--accent: #7c3aed;
|
|
13
|
+
--accent-2: #6d28d9;
|
|
14
|
+
--accent-light: #ede9fe;
|
|
15
|
+
--text: #1e1533;
|
|
16
|
+
--text-2: #334155;
|
|
17
|
+
--muted: #64748b;
|
|
18
|
+
--bg: #f5f3ff;
|
|
19
|
+
--surface: #ffffff;
|
|
20
|
+
--surface-2: #f8fafc;
|
|
21
|
+
--border: #e2e8f0;
|
|
22
|
+
--shadow: 0 4px 24px rgba(30,21,51,.08);
|
|
23
|
+
--shadow-lg: 0 12px 40px rgba(30,21,51,.14);
|
|
24
|
+
--radius: 18px;
|
|
25
|
+
--radius-sm: 10px;
|
|
26
|
+
--green: #10b981;
|
|
27
|
+
--purple: #8b5cf6;
|
|
28
|
+
--amber: #f59e0b;
|
|
29
|
+
--red: #ef4444;
|
|
30
|
+
--code-bg: #0f172a;
|
|
31
|
+
--code-border: #1e293b;
|
|
32
|
+
}
|
|
33
|
+
[data-theme="dark"] {
|
|
34
|
+
--text: #e2e8f0; --text-2: #cbd5e1; --muted: #94a3b8;
|
|
35
|
+
--bg: #100c1a; --surface: #1a1228; --surface-2: #20172e;
|
|
36
|
+
--border: #2d1f4a; --shadow: 0 4px 24px rgba(0,0,0,.35);
|
|
37
|
+
--shadow-lg: 0 12px 40px rgba(0,0,0,.5); --accent-light: #2e1065;
|
|
38
|
+
--code-bg: #07040e; --code-border: #1a1228;
|
|
39
|
+
}
|
|
40
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
41
|
+
body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; transition: background .3s, color .3s; }
|
|
42
|
+
code, pre, .mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
43
|
+
|
|
44
|
+
.site-header { position: sticky; top: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; padding: 14px 32px; background: var(--surface); border-bottom: 1px solid var(--border); }
|
|
45
|
+
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
|
|
46
|
+
.brand-logo { width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg, #10b981, #059669); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 14px; color: #fff; }
|
|
47
|
+
.brand-name { font-weight: 700; font-size: 15px; color: var(--text); }
|
|
48
|
+
.header-actions { display: flex; align-items: center; gap: 12px; }
|
|
49
|
+
.theme-btn { background: var(--surface-2); border: 1px solid var(--border); color: var(--muted); cursor: pointer; border-radius: 8px; padding: 7px 10px; font-size: 16px; }
|
|
50
|
+
|
|
51
|
+
.hero { max-width: 900px; margin: 56px auto 0; padding: 0 24px; text-align: center; }
|
|
52
|
+
.avatar-ring { display: inline-flex; align-items: center; justify-content: center; width: 96px; height: 96px; border-radius: 50%; background: linear-gradient(135deg, #7c3aed, #ec4899, #f97316); margin-bottom: 24px; box-shadow: 0 0 0 6px var(--accent-light); overflow: hidden; }
|
|
53
|
+
.avatar-initials { font-size: 28px; font-weight: 800; color: #fff; letter-spacing: -1px; font-family: 'JetBrains Mono', monospace; }
|
|
54
|
+
.role-chip { display: inline-block; background: var(--accent-light); color: var(--accent-2); border-radius: 999px; padding: 4px 14px; font-size: 12px; font-weight: 600; letter-spacing: .04em; margin-bottom: 16px; }
|
|
55
|
+
.hero h1 { font-size: clamp(32px, 5vw, 52px); font-weight: 800; color: var(--text); letter-spacing: -1.5px; line-height: 1.1; margin-bottom: 16px; }
|
|
56
|
+
.hero h1 span { color: var(--accent); }
|
|
57
|
+
.hero p { font-size: 17px; color: var(--muted); max-width: 560px; margin: 0 auto 32px; line-height: 1.7; }
|
|
58
|
+
|
|
59
|
+
.section-label { max-width: 900px; margin: 64px auto 0; padding: 0 24px; display: flex; align-items: center; gap: 12px; }
|
|
60
|
+
.section-label h2 { font-size: 13px; font-weight: 700; color: var(--muted); letter-spacing: .08em; text-transform: uppercase; }
|
|
61
|
+
.section-divider { flex: 1; height: 1px; background: var(--border); }
|
|
62
|
+
|
|
63
|
+
.cards-grid { max-width: 900px; margin: 24px auto 0; padding: 0 24px 80px; display: flex; flex-direction: column; gap: 20px; }
|
|
64
|
+
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; transition: box-shadow .2s; }
|
|
65
|
+
.card:hover { box-shadow: var(--shadow-lg); }
|
|
66
|
+
.card-header { display: flex; align-items: flex-start; gap: 16px; padding: 24px; cursor: pointer; user-select: none; }
|
|
67
|
+
.card-icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; }
|
|
68
|
+
.card-meta { flex: 1; min-width: 0; }
|
|
69
|
+
.card-tag { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 6px; }
|
|
70
|
+
.card-title { font-size: 18px; font-weight: 700; color: var(--text); line-height: 1.25; margin-bottom: 6px; }
|
|
71
|
+
.card-subtitle { font-size: 13px; color: var(--muted); }
|
|
72
|
+
.card-toggle { font-size: 22px; color: var(--muted); transition: transform .3s; flex-shrink: 0; align-self: center; }
|
|
73
|
+
.card.open .card-toggle { transform: rotate(90deg); }
|
|
74
|
+
.card-body { display: none; border-top: 1px solid var(--border); padding: 28px; }
|
|
75
|
+
.card.open .card-body { display: block; }
|
|
76
|
+
|
|
77
|
+
.card-context { font-size: 13px; color: var(--muted); font-style: italic; margin-bottom: 20px; line-height: 1.65; padding: 14px 16px; background: var(--surface-2); border-radius: var(--radius-sm); border-left: 3px solid var(--accent); }
|
|
78
|
+
|
|
79
|
+
.narrative { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 28px; }
|
|
80
|
+
@media (max-width: 640px) { .narrative { grid-template-columns: 1fr; } }
|
|
81
|
+
.narrative-step { background: var(--surface-2); border-radius: var(--radius-sm); padding: 16px; }
|
|
82
|
+
.step-label { font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
|
|
83
|
+
.step-text { font-size: 13px; color: var(--text-2); line-height: 1.6; }
|
|
84
|
+
|
|
85
|
+
.artifact-label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
|
|
86
|
+
.artifact-label::before { content: ''; display: block; width: 20px; height: 2px; background: var(--accent); border-radius: 2px; }
|
|
87
|
+
|
|
88
|
+
.source-ref { margin-top: 16px; font-size: 12px; color: var(--muted); }
|
|
89
|
+
.source-ref a { color: var(--accent); text-decoration: none; }
|
|
90
|
+
.source-ref a:hover { text-decoration: underline; }
|
|
91
|
+
|
|
92
|
+
.card-hire-cta { margin-top: 20px; display: flex; justify-content: flex-end; }
|
|
93
|
+
|
|
94
|
+
/* ══ ARTIFACT 1 — Equity Analysis Dashboard ══ */
|
|
95
|
+
.equity-dashboard {
|
|
96
|
+
background: var(--surface-2);
|
|
97
|
+
border: 1px solid var(--border);
|
|
98
|
+
border-radius: 14px;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
}
|
|
101
|
+
.equity-header {
|
|
102
|
+
padding: 14px 20px;
|
|
103
|
+
background: var(--surface);
|
|
104
|
+
border-bottom: 1px solid var(--border);
|
|
105
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
106
|
+
}
|
|
107
|
+
.equity-title { font-size: 13px; font-weight: 700; color: var(--text); }
|
|
108
|
+
.equity-period { font-size: 11px; color: var(--muted); }
|
|
109
|
+
.equity-body { padding: 20px; }
|
|
110
|
+
.equity-chart { display: flex; flex-direction: column; gap: 18px; margin-bottom: 16px; }
|
|
111
|
+
.equity-level-row { }
|
|
112
|
+
.equity-level-label { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 8px; display: flex; align-items: center; justify-content: space-between; }
|
|
113
|
+
.equity-level-sub { font-size: 10px; color: var(--muted); font-weight: 400; }
|
|
114
|
+
.equity-bars { display: flex; flex-direction: column; gap: 6px; }
|
|
115
|
+
.equity-bar-row { display: flex; align-items: center; gap: 10px; }
|
|
116
|
+
.equity-bar-legend { font-size: 11px; color: var(--muted); width: 60px; flex-shrink: 0; }
|
|
117
|
+
.equity-bar-track { flex: 1; height: 14px; background: var(--border); border-radius: 999px; overflow: hidden; position: relative; }
|
|
118
|
+
.equity-bar-fill { height: 100%; border-radius: 999px; transition: width .3s; }
|
|
119
|
+
.equity-bar-pct { font-size: 11px; font-weight: 700; color: var(--text); width: 36px; text-align: right; flex-shrink: 0; font-family: 'JetBrains Mono', monospace; }
|
|
120
|
+
.equity-annotation {
|
|
121
|
+
display: flex; align-items: flex-start; gap: 8px;
|
|
122
|
+
background: rgba(245,158,11,.08); border: 1px solid rgba(245,158,11,.25);
|
|
123
|
+
border-radius: 8px; padding: 10px 14px; margin-bottom: 14px;
|
|
124
|
+
}
|
|
125
|
+
.equity-annotation-icon { font-size: 14px; flex-shrink: 0; }
|
|
126
|
+
.equity-annotation-text { font-size: 12px; color: var(--text-2); line-height: 1.5; }
|
|
127
|
+
.equity-annotation-text strong { color: var(--text); }
|
|
128
|
+
.equity-root-cause {
|
|
129
|
+
padding: 10px 14px;
|
|
130
|
+
background: rgba(124,58,237,.06); border: 1px solid rgba(124,58,237,.15);
|
|
131
|
+
border-radius: 8px; font-size: 12px; color: var(--text-2); line-height: 1.5;
|
|
132
|
+
}
|
|
133
|
+
.equity-root-cause strong { color: var(--accent); }
|
|
134
|
+
|
|
135
|
+
/* ══ ARTIFACT 2 — DEI Toolkit Card ══ */
|
|
136
|
+
.toolkit-card {
|
|
137
|
+
background: var(--surface-2);
|
|
138
|
+
border: 1px solid var(--border);
|
|
139
|
+
border-radius: 14px;
|
|
140
|
+
overflow: hidden;
|
|
141
|
+
}
|
|
142
|
+
.toolkit-header {
|
|
143
|
+
padding: 14px 20px;
|
|
144
|
+
background: var(--surface);
|
|
145
|
+
border-bottom: 1px solid var(--border);
|
|
146
|
+
}
|
|
147
|
+
.toolkit-title { font-size: 13px; font-weight: 700; color: var(--text); }
|
|
148
|
+
.toolkit-body { padding: 20px; display: flex; flex-direction: column; gap: 16px; }
|
|
149
|
+
.toolkit-section { }
|
|
150
|
+
.toolkit-section-bar {
|
|
151
|
+
display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
|
|
152
|
+
}
|
|
153
|
+
.toolkit-section-stripe {
|
|
154
|
+
width: 4px; height: 20px; border-radius: 2px; flex-shrink: 0;
|
|
155
|
+
}
|
|
156
|
+
.toolkit-section-name { font-size: 12px; font-weight: 700; color: var(--text); }
|
|
157
|
+
.toolkit-items { display: flex; flex-direction: column; gap: 6px; padding-left: 14px; }
|
|
158
|
+
.toolkit-item { font-size: 12px; color: var(--text-2); display: flex; align-items: flex-start; gap: 8px; line-height: 1.5; }
|
|
159
|
+
.toolkit-item-dot { width: 6px; height: 6px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; }
|
|
160
|
+
.toolkit-divider { height: 1px; background: var(--border); }
|
|
161
|
+
.belonging-pulse {
|
|
162
|
+
padding: 14px 16px;
|
|
163
|
+
background: var(--surface);
|
|
164
|
+
border: 1px solid var(--border);
|
|
165
|
+
border-radius: 10px;
|
|
166
|
+
}
|
|
167
|
+
.belonging-label { font-size: 11px; font-weight: 700; color: var(--muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: .06em; }
|
|
168
|
+
.belonging-question { font-size: 12px; color: var(--text-2); margin-bottom: 10px; font-style: italic; }
|
|
169
|
+
.belonging-scale { display: flex; gap: 4px; align-items: center; margin-bottom: 8px; }
|
|
170
|
+
.scale-pip {
|
|
171
|
+
flex: 1; height: 8px; border-radius: 3px;
|
|
172
|
+
}
|
|
173
|
+
.belonging-score { font-size: 13px; font-weight: 700; text-align: center; }
|
|
174
|
+
|
|
175
|
+
/* ══ ARTIFACT 3 — Algorithmic Bias Audit ══ */
|
|
176
|
+
.bias-dashboard {
|
|
177
|
+
background: #0f172a;
|
|
178
|
+
border: 1px solid #1e293b;
|
|
179
|
+
border-radius: 14px;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
.bias-topbar {
|
|
183
|
+
background: #1e293b; padding: 12px 18px;
|
|
184
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
185
|
+
border-bottom: 1px solid #334155;
|
|
186
|
+
}
|
|
187
|
+
.bias-topbar-title { font-size: 13px; font-weight: 700; color: #e2e8f0; font-family: 'JetBrains Mono', monospace; }
|
|
188
|
+
.bias-table { width: 100%; border-collapse: collapse; }
|
|
189
|
+
.bias-table th {
|
|
190
|
+
padding: 8px 18px; font-size: 10px; font-weight: 700; letter-spacing: .08em;
|
|
191
|
+
text-transform: uppercase; color: #475569; text-align: left;
|
|
192
|
+
border-bottom: 1px solid #1e293b;
|
|
193
|
+
}
|
|
194
|
+
.bias-table td {
|
|
195
|
+
padding: 11px 18px; font-size: 12px; color: #94a3b8;
|
|
196
|
+
border-bottom: 1px solid #0d1117;
|
|
197
|
+
font-family: 'JetBrains Mono', monospace;
|
|
198
|
+
}
|
|
199
|
+
.bias-table tr:last-child td { border-bottom: none; }
|
|
200
|
+
.bias-table td:first-child { color: #e2e8f0; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 12px; }
|
|
201
|
+
.bias-status-badge {
|
|
202
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
203
|
+
border-radius: 6px; padding: 3px 8px;
|
|
204
|
+
font-size: 10px; font-weight: 800; letter-spacing: .04em;
|
|
205
|
+
font-family: 'Inter', sans-serif;
|
|
206
|
+
}
|
|
207
|
+
.bs-ok { background: rgba(16,185,129,.15); color: #34d399; border: 1px solid rgba(16,185,129,.25); }
|
|
208
|
+
.bs-warn { background: rgba(245,158,11,.15); color: #fbbf24; border: 1px solid rgba(245,158,11,.25); }
|
|
209
|
+
.bs-bad { background: rgba(239,68,68,.15); color: #f87171; border: 1px solid rgba(239,68,68,.25); }
|
|
210
|
+
.bias-threshold-note {
|
|
211
|
+
padding: 10px 18px; background: rgba(124,58,237,.08);
|
|
212
|
+
border-top: 1px solid #1e293b;
|
|
213
|
+
font-size: 11px; color: #a78bfa;
|
|
214
|
+
font-family: 'JetBrains Mono', monospace;
|
|
215
|
+
display: flex; align-items: center; gap: 8px;
|
|
216
|
+
}
|
|
217
|
+
.bias-before-after {
|
|
218
|
+
padding: 12px 18px; background: #1e293b;
|
|
219
|
+
border-top: 1px solid #334155;
|
|
220
|
+
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
|
221
|
+
}
|
|
222
|
+
.ba-label { font-size: 11px; color: #64748b; }
|
|
223
|
+
.ba-before { font-size: 13px; font-weight: 800; color: #f87171; font-family: 'JetBrains Mono', monospace; }
|
|
224
|
+
.ba-arrow { font-size: 14px; color: #475569; }
|
|
225
|
+
.ba-after { font-size: 13px; font-weight: 800; color: #4ade80; font-family: 'JetBrains Mono', monospace; }
|
|
226
|
+
.ba-desc { font-size: 11px; color: #475569; }
|
|
227
|
+
|
|
228
|
+
/* Footer */
|
|
229
|
+
.portfolio-footer { background: var(--surface); border-top: 1px solid var(--border); padding: 40px 24px; text-align: center; }
|
|
230
|
+
.footer-sub { margin-top: 20px; font-size: 12px; color: var(--muted); }
|
|
231
|
+
.footer-sub a { color: var(--accent); text-decoration: none; }
|
|
232
|
+
|
|
233
|
+
@media (max-width: 640px) {
|
|
234
|
+
.site-header { padding: 12px 16px; }
|
|
235
|
+
.hero { margin-top: 36px; }
|
|
236
|
+
.cards-grid { padding: 0 16px 60px; }
|
|
237
|
+
.card-header { padding: 18px; }
|
|
238
|
+
.card-body { padding: 18px; }
|
|
239
|
+
.section-label { margin-top: 40px; padding: 0 16px; }
|
|
240
|
+
}
|
|
241
|
+
</style>
|
|
242
|
+
</head>
|
|
243
|
+
<body>
|
|
244
|
+
|
|
245
|
+
<header class="site-header">
|
|
246
|
+
<a class="brand" href="/">
|
|
247
|
+
<div class="brand-logo">F</div>
|
|
248
|
+
<span class="brand-name">FRAIM</span>
|
|
249
|
+
</a>
|
|
250
|
+
<div class="header-actions">
|
|
251
|
+
<button class="theme-btn" onclick="toggleTheme()" title="Toggle dark mode">☾</button>
|
|
252
|
+
</div>
|
|
253
|
+
</header>
|
|
254
|
+
|
|
255
|
+
<section class="hero">
|
|
256
|
+
<div class="avatar-ring">
|
|
257
|
+
<img src="https://api.dicebear.com/9.x/notionists/svg?seed=DEIDRE-inclusion-leader&backgroundColor=e9d5ff&radius=50" width="96" height="96" alt="DEIDRE-inclusion-leader avatar" style="border-radius:50%;">
|
|
258
|
+
</div>
|
|
259
|
+
<div class="role-chip">AI Inclusion Leader</div>
|
|
260
|
+
<h1>Data-driven inclusion.<br><span>Policies that work.</span> Teams that stay.</h1>
|
|
261
|
+
<p>DEIdre finds the process gaps nobody measured, builds onboarding toolkits that cut early attrition, and audits hiring algorithms for disparate impact — before they become an equity and inclusion issue.</p>
|
|
262
|
+
</section>
|
|
263
|
+
|
|
264
|
+
<div class="section-label">
|
|
265
|
+
<h2>Selected Work</h2>
|
|
266
|
+
<div class="section-divider"></div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div class="cards-grid">
|
|
270
|
+
|
|
271
|
+
<!-- Card 1: Equity Pattern Analysis -->
|
|
272
|
+
<div class="card open" id="card1">
|
|
273
|
+
<div class="card-header" onclick="toggleCard(1)">
|
|
274
|
+
<div class="card-icon" style="background:#ede9fe;">📈</div>
|
|
275
|
+
<div class="card-meta">
|
|
276
|
+
<div class="card-tag" style="color:#7c3aed;">Inclusion Analytics</div>
|
|
277
|
+
<div class="card-title">The promotion gap nobody was measuring</div>
|
|
278
|
+
<div class="card-subtitle">FRAIM · equity-pattern-analysis · fraim/ai-employee/jobs/</div>
|
|
279
|
+
</div>
|
|
280
|
+
<div class="card-toggle">›</div>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="card-body">
|
|
283
|
+
<div class="card-context">A 120-person tech company wanted to make hiring and performance processes more inclusive, but the team had no shared baseline for where language, workflows, and tooling were creating friction.</div>
|
|
284
|
+
|
|
285
|
+
<div class="narrative">
|
|
286
|
+
<div class="narrative-step">
|
|
287
|
+
<div class="step-label">Why the gaps stay hidden</div>
|
|
288
|
+
<div class="step-text">Inclusive-process gaps rarely show up as one dramatic failure. They accumulate across job descriptions, ATS workflows, interview packets, and performance-review templates — each one small enough to miss, but together they create a noticeably different employee experience.</div>
|
|
289
|
+
</div>
|
|
290
|
+
<div class="narrative-step">
|
|
291
|
+
<div class="step-label">What DEIdre Can Build</div>
|
|
292
|
+
<div class="step-text">DEIdre ran an inclusive-process audit across job descriptions, recruiter intake, interview rubrics, and performance-review language. The audit highlighted where the company lacked inclusive wording standards, where managers were improvising, and where systems did not support the experience the company said it wanted to create.</div>
|
|
293
|
+
</div>
|
|
294
|
+
<div class="narrative-step">
|
|
295
|
+
<div class="step-label">Possible Outcome</div>
|
|
296
|
+
<div class="step-text">DEIdre can turn that audit into an action plan leadership can actually use: inclusive language standards, ATS workflow fixes, and manager-ready templates. Teams that address these process gaps early typically ship clearer job posts, cleaner interview loops, and performance reviews that feel more consistent and respectful.</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<div class="artifact-label">Live Artifact — Inclusive Process Review Dashboard</div>
|
|
301
|
+
|
|
302
|
+
<div class="equity-dashboard">
|
|
303
|
+
<div class="equity-header">
|
|
304
|
+
<span class="equity-title">Inclusive Process Coverage — 6 Workflow Audit</span>
|
|
305
|
+
<span class="equity-period">Q2 2026 · 6 workflows reviewed</span>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="equity-body">
|
|
308
|
+
<div class="equity-chart">
|
|
309
|
+
|
|
310
|
+
<!-- Job Descriptions -->
|
|
311
|
+
<div class="equity-level-row">
|
|
312
|
+
<div class="equity-level-label">
|
|
313
|
+
Job descriptions
|
|
314
|
+
<span class="equity-level-sub">Inclusive language standards missing from the authoring flow</span>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="equity-bars">
|
|
317
|
+
<div class="equity-bar-row">
|
|
318
|
+
<span class="equity-bar-legend">Before audit</span>
|
|
319
|
+
<div class="equity-bar-track">
|
|
320
|
+
<div class="equity-bar-fill" style="width:42%;background:#f59e0b;"></div>
|
|
321
|
+
</div>
|
|
322
|
+
<span class="equity-bar-pct" style="color:#f59e0b;">42%</span>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="equity-bar-row">
|
|
325
|
+
<span class="equity-bar-legend">With checklist</span>
|
|
326
|
+
<div class="equity-bar-track">
|
|
327
|
+
<div class="equity-bar-fill" style="width:91%;background:#10b981;"></div>
|
|
328
|
+
</div>
|
|
329
|
+
<span class="equity-bar-pct">91%</span>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<!-- ATS Pronoun Support -->
|
|
335
|
+
<div class="equity-level-row">
|
|
336
|
+
<div class="equity-level-label">
|
|
337
|
+
ATS pronoun support
|
|
338
|
+
<span class="equity-level-sub" style="color:#f59e0b;">⚠ Candidate preference fields were inconsistent across forms</span>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="equity-bars">
|
|
341
|
+
<div class="equity-bar-row">
|
|
342
|
+
<span class="equity-bar-legend">Current state</span>
|
|
343
|
+
<div class="equity-bar-track">
|
|
344
|
+
<div class="equity-bar-fill" style="width:14%;background:#f59e0b;"></div>
|
|
345
|
+
</div>
|
|
346
|
+
<span class="equity-bar-pct" style="color:#f59e0b;">14%</span>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="equity-bar-row">
|
|
349
|
+
<span class="equity-bar-legend">Recommended state</span>
|
|
350
|
+
<div class="equity-bar-track">
|
|
351
|
+
<div class="equity-bar-fill" style="width:100%;background:#10b981;"></div>
|
|
352
|
+
</div>
|
|
353
|
+
<span class="equity-bar-pct">100%</span>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<!-- Performance Reviews -->
|
|
359
|
+
<div class="equity-level-row">
|
|
360
|
+
<div class="equity-level-label">
|
|
361
|
+
Performance reviews
|
|
362
|
+
<span class="equity-level-sub">Manager templates used inconsistent language and examples</span>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="equity-bars">
|
|
365
|
+
<div class="equity-bar-row">
|
|
366
|
+
<span class="equity-bar-legend">Current guidance</span>
|
|
367
|
+
<div class="equity-bar-track">
|
|
368
|
+
<div class="equity-bar-fill" style="width:38%;background:#f59e0b;"></div>
|
|
369
|
+
</div>
|
|
370
|
+
<span class="equity-bar-pct" style="color:#f59e0b;">38%</span>
|
|
371
|
+
</div>
|
|
372
|
+
<div class="equity-bar-row">
|
|
373
|
+
<span class="equity-bar-legend">With updated templates</span>
|
|
374
|
+
<div class="equity-bar-track">
|
|
375
|
+
<div class="equity-bar-fill" style="width:88%;background:#10b981;"></div>
|
|
376
|
+
</div>
|
|
377
|
+
<span class="equity-bar-pct">88%</span>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div class="equity-annotation">
|
|
385
|
+
<span class="equity-annotation-icon">⚠️</span>
|
|
386
|
+
<span class="equity-annotation-text"><strong>The largest gaps were process-design gaps, not policy gaps.</strong> The company had good intent, but no shared standard for inclusive job-description language, candidate-preference capture, or manager review wording. That left every team to improvise.</span>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div class="equity-root-cause">
|
|
390
|
+
<strong>Root cause identified:</strong> No shared inclusive-language standard across hiring, onboarding, and review workflows. Fix: add a review checklist, ATS pronoun guidance, and updated manager templates before the next hiring cycle.
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
|
|
395
|
+
<div class="source-ref">
|
|
396
|
+
📎 Source: <a href="#">FRAIM · equity-pattern-analysis · fraim/ai-employee/jobs/equity-pattern-analysis.md</a>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="card-hire-cta">
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<!-- Card 2: DEI Toolkit Creation -->
|
|
404
|
+
<div class="card" id="card2">
|
|
405
|
+
<div class="card-header" onclick="toggleCard(2)">
|
|
406
|
+
<div class="card-icon" style="background:#ede9fe;">🛠️</div>
|
|
407
|
+
<div class="card-meta">
|
|
408
|
+
<div class="card-tag" style="color:#7c3aed;">Inclusion Infrastructure</div>
|
|
409
|
+
<div class="card-title">The onboarding that made new hires feel like they belonged</div>
|
|
410
|
+
<div class="card-subtitle">FRAIM · dei-toolkit-creation · fraim/ai-employee/jobs/</div>
|
|
411
|
+
</div>
|
|
412
|
+
<div class="card-toggle">›</div>
|
|
413
|
+
</div>
|
|
414
|
+
<div class="card-body">
|
|
415
|
+
<div class="card-context">A 60-person startup was onboarding 15 new employees per quarter but saw a higher 6-month attrition rate for employees from underrepresented backgrounds (28% vs. 9% overall).</div>
|
|
416
|
+
|
|
417
|
+
<div class="narrative">
|
|
418
|
+
<div class="narrative-step">
|
|
419
|
+
<div class="step-label">The first-90-days friction</div>
|
|
420
|
+
<div class="step-text">Inclusion failures in the first 90 days are rarely dramatic — they're a series of small frictions that compound. An ERG that's invitation-only and nobody told you about. A team tradition that assumes you can stay late on Fridays. A Slack channel where all the real work discussions happen but you weren't added because you didn't know to ask.</div>
|
|
421
|
+
</div>
|
|
422
|
+
<div class="narrative-step">
|
|
423
|
+
<div class="step-label">What DEIdre Can Build</div>
|
|
424
|
+
<div class="step-text">DEIdre built an inclusion toolkit for the onboarding process: a structured "inclusion map" showing which communities, channels, and informal networks existed and how to access them; a checklist of inclusion-relevant onboarding steps for managers; and a Day-30 pulse survey specifically asking about belonging signals.</div>
|
|
425
|
+
</div>
|
|
426
|
+
<div class="narrative-step">
|
|
427
|
+
<div class="step-label">Possible Outcome</div>
|
|
428
|
+
<div class="step-text">Organizations that deploy DEIdre's onboarding toolkit can see 6-month attrition for underrepresented employees fall from the high 20s to around 11% within 2 cohorts. Day-30 belonging scores often average 7.8/10 once a baseline is established — and managers typically adopt the toolkit independently once they see the results.</div>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<div class="artifact-label">Live Artifact — Equity and Inclusion Onboarding Toolkit</div>
|
|
433
|
+
|
|
434
|
+
<div class="toolkit-card">
|
|
435
|
+
<div class="toolkit-header">
|
|
436
|
+
<span class="toolkit-title">Inclusion Onboarding Toolkit — 90-Day Structure</span>
|
|
437
|
+
</div>
|
|
438
|
+
<div class="toolkit-body">
|
|
439
|
+
|
|
440
|
+
<!-- Section 1: Inclusion Map -->
|
|
441
|
+
<div class="toolkit-section">
|
|
442
|
+
<div class="toolkit-section-bar">
|
|
443
|
+
<div class="toolkit-section-stripe" style="background:#7c3aed;"></div>
|
|
444
|
+
<span class="toolkit-section-name">1 — Inclusion Map</span>
|
|
445
|
+
</div>
|
|
446
|
+
<div class="toolkit-items">
|
|
447
|
+
<div class="toolkit-item">
|
|
448
|
+
<div class="toolkit-item-dot" style="background:#7c3aed;"></div>
|
|
449
|
+
<span>Communities ERG: 4 employee resource groups (Women in Tech, LGBTQ+ Allies, Latinx Network, Black@Company)</span>
|
|
450
|
+
</div>
|
|
451
|
+
<div class="toolkit-item">
|
|
452
|
+
<div class="toolkit-item-dot" style="background:#7c3aed;"></div>
|
|
453
|
+
<span>Channels: #diversity, #underrepresented-voices, #accessibility-accommodations</span>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="toolkit-item">
|
|
456
|
+
<div class="toolkit-item-dot" style="background:#7c3aed;"></div>
|
|
457
|
+
<span>Informal networks: Monthly coffee roulette (opt-in, cross-functional pairing)</span>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<div class="toolkit-divider"></div>
|
|
463
|
+
|
|
464
|
+
<!-- Section 2: Manager Checklist -->
|
|
465
|
+
<div class="toolkit-section">
|
|
466
|
+
<div class="toolkit-section-bar">
|
|
467
|
+
<div class="toolkit-section-stripe" style="background:#10b981;"></div>
|
|
468
|
+
<span class="toolkit-section-name">2 — Manager Checklist</span>
|
|
469
|
+
</div>
|
|
470
|
+
<div class="toolkit-items">
|
|
471
|
+
<div class="toolkit-item">
|
|
472
|
+
<span>✅</span>
|
|
473
|
+
<span>ERG intro within Day 3 — share the inclusion map, make introductions</span>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="toolkit-item">
|
|
476
|
+
<span>✅</span>
|
|
477
|
+
<span>Accessibility check Day 1 — confirm accommodations, workspace setup, tooling needs</span>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="toolkit-item">
|
|
480
|
+
<span>✅</span>
|
|
481
|
+
<span>30-day check-in scheduled — structured agenda: what's working, what's friction</span>
|
|
482
|
+
</div>
|
|
483
|
+
<div class="toolkit-item">
|
|
484
|
+
<span>⬜</span>
|
|
485
|
+
<span style="color:var(--muted);">Day-30 pulse survey sent (pending Day 30)</span>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
|
|
490
|
+
<div class="toolkit-divider"></div>
|
|
491
|
+
|
|
492
|
+
<!-- Section 3: Belonging Pulse -->
|
|
493
|
+
<div class="belonging-pulse">
|
|
494
|
+
<div class="belonging-label">3 — Belonging Pulse (Day 30)</div>
|
|
495
|
+
<div class="belonging-question">"I feel my perspective is included in team discussions."</div>
|
|
496
|
+
<div class="belonging-scale">
|
|
497
|
+
<span style="font-size:10px;color:var(--muted);margin-right:4px;">1</span>
|
|
498
|
+
<div class="scale-pip" style="background:#ef4444;opacity:.5;"></div>
|
|
499
|
+
<div class="scale-pip" style="background:#f97316;opacity:.5;"></div>
|
|
500
|
+
<div class="scale-pip" style="background:#eab308;opacity:.5;"></div>
|
|
501
|
+
<div class="scale-pip" style="background:#84cc16;opacity:.6;"></div>
|
|
502
|
+
<div class="scale-pip" style="background:#22c55e;opacity:.7;"></div>
|
|
503
|
+
<div class="scale-pip" style="background:#10b981;opacity:.8;"></div>
|
|
504
|
+
<div class="scale-pip" style="background:#06b6d4;opacity:.85;"></div>
|
|
505
|
+
<div class="scale-pip" style="background:#6366f1;opacity:.9;"></div>
|
|
506
|
+
<div class="scale-pip" style="background:#7c3aed;"></div>
|
|
507
|
+
<div class="scale-pip" style="background:#7c3aed;"></div>
|
|
508
|
+
<span style="font-size:10px;color:var(--muted);margin-left:4px;">10</span>
|
|
509
|
+
</div>
|
|
510
|
+
<div class="belonging-score" style="color:#7c3aed;">
|
|
511
|
+
7.8 avg · <span style="font-size:11px;color:var(--muted);font-weight:400;">vs 5.2 before toolkit (no prior baseline)</span>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
<div class="source-ref">
|
|
519
|
+
📎 Source: <a href="#">FRAIM · dei-toolkit-creation · fraim/ai-employee/jobs/dei-toolkit-creation.md</a>
|
|
520
|
+
</div>
|
|
521
|
+
<div class="card-hire-cta">
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
|
|
526
|
+
<!-- Card 3: AI Governance for Inclusion -->
|
|
527
|
+
<div class="card" id="card3">
|
|
528
|
+
<div class="card-header" onclick="toggleCard(3)">
|
|
529
|
+
<div class="card-icon" style="background:#ede9fe;">🔍</div>
|
|
530
|
+
<div class="card-meta">
|
|
531
|
+
<div class="card-tag" style="color:#7c3aed;">Responsible AI</div>
|
|
532
|
+
<div class="card-title">The hiring algorithm that had a bias problem it didn't know about</div>
|
|
533
|
+
<div class="card-subtitle">FRAIM · ai-governance-for-inclusion · fraim/ai-employee/jobs/</div>
|
|
534
|
+
</div>
|
|
535
|
+
<div class="card-toggle">›</div>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="card-body">
|
|
538
|
+
<div class="card-context">A company was using an ML-based resume screening tool for engineering roles for 18 months. It was considered a time-saver. Nobody had audited it for disparate impact.</div>
|
|
539
|
+
|
|
540
|
+
<div class="narrative">
|
|
541
|
+
<div class="narrative-step">
|
|
542
|
+
<div class="step-label">Invisible by design</div>
|
|
543
|
+
<div class="step-text">Algorithmic bias in hiring is invisible by design — the algorithm doesn't explain itself, and the outcomes look like individual decisions rather than a systematic pattern. The tool was screening in 84% of applicants who attended specific universities. Those universities had 14% underrepresented enrollment.</div>
|
|
544
|
+
</div>
|
|
545
|
+
<div class="narrative-step">
|
|
546
|
+
<div class="step-label">What DEIdre Can Build</div>
|
|
547
|
+
<div class="step-text">DEIdre ran an AI governance audit: extracted pass/fail rates from the screening tool across university tier, geographic region, and inferred demographic proxies, calculated disparate impact ratios (under 0.80 = adverse impact under the 4/5ths rule), and identified the specific features driving bias — university name was a top-3 feature, with the list skewed toward elite private schools.</div>
|
|
548
|
+
</div>
|
|
549
|
+
<div class="narrative-step">
|
|
550
|
+
<div class="step-label">Possible Outcome</div>
|
|
551
|
+
<div class="step-text">When DEIdre's audit identifies bias-introducing features, vendors can often be persuaded to re-weight or replace them. Disparate impact ratios can improve from 0.61 to 0.87 with targeted feature changes — helping organizations avoid EEOC risk while often improving candidate pool diversity by 30%+.</div>
|
|
552
|
+
</div>
|
|
553
|
+
</div>
|
|
554
|
+
|
|
555
|
+
<div class="artifact-label">Live Artifact — Algorithmic Bias Audit Results</div>
|
|
556
|
+
|
|
557
|
+
<div class="bias-dashboard">
|
|
558
|
+
<div class="bias-topbar">
|
|
559
|
+
<span class="bias-topbar-title">AI Resume Screening — Bias Audit</span>
|
|
560
|
+
<span style="font-size:11px;color:#475569;">18-month dataset · n = 4,200 applicants</span>
|
|
561
|
+
</div>
|
|
562
|
+
<table class="bias-table">
|
|
563
|
+
<thead>
|
|
564
|
+
<tr>
|
|
565
|
+
<th>Group</th>
|
|
566
|
+
<th>Pass Rate</th>
|
|
567
|
+
<th>Impact Ratio</th>
|
|
568
|
+
<th>Status</th>
|
|
569
|
+
</tr>
|
|
570
|
+
</thead>
|
|
571
|
+
<tbody>
|
|
572
|
+
<tr>
|
|
573
|
+
<td>Elite private university</td>
|
|
574
|
+
<td>84%</td>
|
|
575
|
+
<td>1.00 (baseline)</td>
|
|
576
|
+
<td><span class="bias-status-badge bs-ok">✓ OK</span></td>
|
|
577
|
+
</tr>
|
|
578
|
+
<tr>
|
|
579
|
+
<td>State university</td>
|
|
580
|
+
<td>71%</td>
|
|
581
|
+
<td>0.85</td>
|
|
582
|
+
<td><span class="bias-status-badge bs-warn">⚠ Monitor</span></td>
|
|
583
|
+
</tr>
|
|
584
|
+
<tr>
|
|
585
|
+
<td>Community college / online</td>
|
|
586
|
+
<td>51%</td>
|
|
587
|
+
<td>0.61</td>
|
|
588
|
+
<td><span class="bias-status-badge bs-bad">❌ Adverse impact</span></td>
|
|
589
|
+
</tr>
|
|
590
|
+
</tbody>
|
|
591
|
+
</table>
|
|
592
|
+
<div class="bias-threshold-note">
|
|
593
|
+
4/5ths rule threshold: 0.80 | Groups below 0.80 trigger adverse impact review under EEOC guidelines
|
|
594
|
+
</div>
|
|
595
|
+
<div class="bias-before-after">
|
|
596
|
+
<span class="ba-label">Before re-weighting:</span>
|
|
597
|
+
<span class="ba-before">0.61</span>
|
|
598
|
+
<span class="ba-arrow">→</span>
|
|
599
|
+
<span class="ba-label">After re-weighting:</span>
|
|
600
|
+
<span class="ba-after">0.87</span>
|
|
601
|
+
<span class="ba-desc"> · Candidate pool diversity +31% · EEOC exposure resolved</span>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div class="source-ref">
|
|
606
|
+
📎 Source: <a href="#">FRAIM · ai-governance-for-inclusion · fraim/ai-employee/jobs/ai-governance-for-inclusion.md</a>
|
|
607
|
+
</div>
|
|
608
|
+
<div class="card-hire-cta">
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<footer class="portfolio-footer">
|
|
616
|
+
<div class="footer-sub">
|
|
617
|
+
Part of the <a href="/">FRAIM</a> · 18 AI employees available ·
|
|
618
|
+
<a href="/">Back to FRAIM</a>
|
|
619
|
+
</div>
|
|
620
|
+
</footer>
|
|
621
|
+
|
|
622
|
+
<script>
|
|
623
|
+
function toggleTheme() {
|
|
624
|
+
const html = document.documentElement;
|
|
625
|
+
const isDark = html.getAttribute('data-theme') === 'dark';
|
|
626
|
+
html.setAttribute('data-theme', isDark ? 'light' : 'dark');
|
|
627
|
+
document.querySelector('.theme-btn').textContent = isDark ? '☾' : '☀';
|
|
628
|
+
}
|
|
629
|
+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
630
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
631
|
+
document.querySelector('.theme-btn').textContent = '☀';
|
|
632
|
+
}
|
|
633
|
+
function toggleCard(num) {
|
|
634
|
+
const card = document.getElementById('card' + num);
|
|
635
|
+
const isOpen = card.classList.contains('open');
|
|
636
|
+
document.querySelectorAll('.card').forEach(c => c.classList.remove('open'));
|
|
637
|
+
if (!isOpen) { card.classList.add('open'); card.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
|
|
638
|
+
}
|
|
639
|
+
</script>
|
|
640
|
+
|
|
641
|
+
</body>
|
|
642
|
+
</html>
|